From 2d98b0d331a9ce30b2c02351220ab03f04fd4254 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 14 May 2021 16:36:42 +0200 Subject: [PATCH 01/54] Uniques: An economically-secure basic-featured NFT pallet --- Cargo.lock | 15 + Cargo.toml | 1 + frame/assets/src/impl_fungibles.rs | 2 +- frame/assets/src/lib.rs | 17 +- frame/uniques/Cargo.toml | 49 + frame/uniques/README.md | 117 +++ frame/uniques/src/benchmarking.rs | 429 +++++++++ frame/uniques/src/functions.rs | 112 +++ frame/uniques/src/impl_non_fungibles.rs | 156 ++++ frame/uniques/src/lib.rs | 1135 +++++++++++++++++++++++ frame/uniques/src/mock.rs | 148 +++ frame/uniques/src/tests.rs | 634 +++++++++++++ frame/uniques/src/types.rs | 110 +++ frame/uniques/src/weights.rs | 333 +++++++ 14 files changed, 3247 insertions(+), 11 deletions(-) create mode 100644 frame/uniques/Cargo.toml create mode 100644 frame/uniques/README.md create mode 100644 frame/uniques/src/benchmarking.rs create mode 100644 frame/uniques/src/functions.rs create mode 100644 frame/uniques/src/impl_non_fungibles.rs create mode 100644 frame/uniques/src/lib.rs create mode 100644 frame/uniques/src/mock.rs create mode 100644 frame/uniques/src/tests.rs create mode 100644 frame/uniques/src/types.rs create mode 100644 frame/uniques/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index 9019acfcbb239..07db654167136 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5564,6 +5564,21 @@ dependencies = [ "sp-storage", ] +[[package]] +name = "pallet-uniques" +version = "3.0.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-utility" version = "3.0.0" diff --git a/Cargo.toml b/Cargo.toml index 9d7017be1d0de..6fd71a9d90a65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -125,6 +125,7 @@ members = [ "frame/transaction-payment/rpc/runtime-api", "frame/treasury", "frame/tips", + "frame/uniques", "frame/utility", "frame/vesting", "primitives/allocator", diff --git a/frame/assets/src/impl_fungibles.rs b/frame/assets/src/impl_fungibles.rs index d0ab13072a88d..256ba5939a77f 100644 --- a/frame/assets/src/impl_fungibles.rs +++ b/frame/assets/src/impl_fungibles.rs @@ -19,7 +19,7 @@ use super::*; -impl, I: 'static> fungibles::Inspect<::AccountId> for Pallet { +impl, I: 'static> non_fungibles::Inspect<::AccountId> for Pallet { type AssetId = T::AssetId; type Balance = T::Balance; diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 3a2b1a6ce21dd..c022a1e9e0ab4 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -132,7 +132,6 @@ mod tests; mod extra_mutator; pub use extra_mutator::*; -mod impl_stored_map; mod impl_fungibles; mod functions; mod types; @@ -410,8 +409,6 @@ pub mod pallet { /// - `owner`: The owner of this class of assets. The owner has full superuser permissions /// over this asset, but may later change and configure the permissions using `transfer_ownership` /// and `set_team`. - /// - `max_zombies`: The total number of accounts which may hold assets in this class yet - /// have no existential deposit. /// - `min_balance`: The minimum balance of this new asset that any single account must /// have. If an account's balance is reduced below this, then it collapses to zero. /// @@ -518,7 +515,7 @@ pub mod pallet { /// - `beneficiary`: The account to be credited with the minted assets. /// - `amount`: The amount of the asset to be minted. /// - /// Emits `Destroyed` event when successful. + /// Emits `Issued` event when successful. /// /// Weight: `O(1)` /// Modes: Pre-existing balance of `beneficiary`; Account pre-existence of `beneficiary`. @@ -583,8 +580,8 @@ pub mod pallet { /// to zero. /// /// Weight: `O(1)` - /// Modes: Pre-existence of `target`; Post-existence of sender; Prior & post zombie-status - /// of sender; Account pre-existence of `target`. + /// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of + /// `target`. #[pallet::weight(T::WeightInfo::transfer())] pub(super) fn transfer( origin: OriginFor, @@ -619,8 +616,8 @@ pub mod pallet { /// to zero. /// /// Weight: `O(1)` - /// Modes: Pre-existence of `target`; Post-existence of sender; Prior & post zombie-status - /// of sender; Account pre-existence of `target`. + /// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of + /// `target`. #[pallet::weight(T::WeightInfo::transfer_keep_alive())] pub(super) fn transfer_keep_alive( origin: OriginFor, @@ -656,8 +653,8 @@ pub mod pallet { /// to zero. /// /// Weight: `O(1)` - /// Modes: Pre-existence of `dest`; Post-existence of `source`; Prior & post zombie-status - /// of `source`; Account pre-existence of `dest`. + /// Modes: Pre-existence of `dest`; Post-existence of `source`; Account pre-existence of + /// `dest`. #[pallet::weight(T::WeightInfo::force_transfer())] pub(super) fn force_transfer( origin: OriginFor, diff --git a/frame/uniques/Cargo.toml b/frame/uniques/Cargo.toml new file mode 100644 index 0000000000000..cbf6cd5008463 --- /dev/null +++ b/frame/uniques/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "pallet-uniques" +version = "3.0.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "Apache-2.0" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME NFT asset management pallet" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } +sp-std = { version = "3.0.0", default-features = false, path = "../../primitives/std" } +sp-core = { version = "3.0.0", default-features = false, path = "../../primitives/core" } +# Needed for various traits. In our case, `OnFinalize`. +sp-runtime = { version = "3.0.0", default-features = false, path = "../../primitives/runtime" } +# Needed for type-safe access to storage DB. +frame-support = { version = "3.0.0", default-features = false, path = "../support" } +# `system` module provides us with all sorts of useful stuff and macros depend on it being around. +frame-system = { version = "3.0.0", default-features = false, path = "../system" } +frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } + +[dev-dependencies] +sp-std = { version = "3.0.0", path = "../../primitives/std" } +sp-core = { version = "3.0.0", path = "../../primitives/core" } +sp-io = { version = "3.0.0", path = "../../primitives/io" } +pallet-balances = { version = "3.0.0", path = "../balances" } + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-std/std", + "sp-core/std", + "sp-runtime/std", + "frame-support/std", + "frame-system/std", + "frame-benchmarking/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "sp-runtime/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/frame/uniques/README.md b/frame/uniques/README.md new file mode 100644 index 0000000000000..f8583a5c91d70 --- /dev/null +++ b/frame/uniques/README.md @@ -0,0 +1,117 @@ +# Assets Module + +A simple, secure module for dealing with fungible assets. + +## Overview + +The Assets module provides functionality for asset management of fungible asset classes +with a fixed supply, including: + +* Asset Issuance +* Asset Transfer +* Asset Destruction + +To use it in your runtime, you need to implement the assets [`assets::Trait`](https://docs.rs/pallet-assets/latest/pallet_assets/trait.Trait.html). + +The supported dispatchable functions are documented in the [`assets::Call`](https://docs.rs/pallet-assets/latest/pallet_assets/enum.Call.html) enum. + +### Terminology + +* **Asset issuance:** The creation of a new asset, whose total supply will belong to the + account that issues the asset. +* **Asset transfer:** The action of transferring assets from one account to another. +* **Asset destruction:** The process of an account removing its entire holding of an asset. +* **Fungible asset:** An asset whose units are interchangeable. +* **Non-fungible asset:** An asset for which each unit has unique characteristics. + +### Goals + +The assets system in Substrate is designed to make the following possible: + +* Issue a unique asset to its creator's account. +* Move assets between accounts. +* Remove an account's balance of an asset when requested by that account's owner and update + the asset's total supply. + +## Interface + +### Dispatchable Functions + +* `issue` - Issues the total supply of a new fungible asset to the account of the caller of the function. +* `transfer` - Transfers an `amount` of units of fungible asset `id` from the balance of +the function caller's account (`origin`) to a `target` account. +* `destroy` - Destroys the entire holding of a fungible asset `id` associated with the account +that called the function. + +Please refer to the [`Call`](https://docs.rs/pallet-assets/latest/pallet_assets/enum.Call.html) enum and its associated variants for documentation on each function. + +### Public Functions + + +* `balance` - Get the asset `id` balance of `who`. +* `total_supply` - Get the total supply of an asset `id`. + +Please refer to the [`Module`](https://docs.rs/pallet-assets/latest/pallet_assets/struct.Module.html) struct for details on publicly available functions. + +## Usage + +The following example shows how to use the Assets module in your runtime by exposing public functions to: + +* Issue a new fungible asset for a token distribution event (airdrop). +* Query the fungible asset holding balance of an account. +* Query the total supply of a fungible asset that has been issued. + +### Prerequisites + +Import the Assets module and types and derive your runtime's configuration traits from the Assets module trait. + +### Simple Code Snippet + +```rust +use pallet_assets as assets; +use frame_support::{decl_module, dispatch, ensure}; +use frame_system::ensure_signed; +use sp_runtime::ArithmeticError; + +pub trait Config: assets::Config { } + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + pub fn issue_token_airdrop(origin) -> dispatch::DispatchResult { + let sender = ensure_signed(origin).map_err(|e| e.as_str())?; + + const ACCOUNT_ALICE: u64 = 1; + const ACCOUNT_BOB: u64 = 2; + const COUNT_AIRDROP_RECIPIENTS: u64 = 2; + const TOKENS_FIXED_SUPPLY: u64 = 100; + + ensure!(!COUNT_AIRDROP_RECIPIENTS.is_zero(), ArithmeticError::DivisionByZero); + + let asset_id = Self::next_asset_id(); + + >::mutate(|asset_id| *asset_id += 1); + >::insert((asset_id, &ACCOUNT_ALICE), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); + >::insert((asset_id, &ACCOUNT_BOB), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); + >::insert(asset_id, TOKENS_FIXED_SUPPLY); + + Self::deposit_event(RawEvent::Issued(asset_id, sender, TOKENS_FIXED_SUPPLY)); + Ok(()) + } + } +} +``` + +## Assumptions + +Below are assumptions that must be held when using this module. If any of +them are violated, the behavior of this module is undefined. + +* The total count of assets should be less than + `Config::AssetId::max_value()`. + +## Related Modules + +* [`System`](https://docs.rs/frame-system/latest/frame_system/) +* [`Support`](https://docs.rs/frame-support/latest/frame_support/) + +License: Apache-2.0 diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs new file mode 100644 index 0000000000000..0d80ec5923d26 --- /dev/null +++ b/frame/uniques/src/benchmarking.rs @@ -0,0 +1,429 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2021 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 sp_std::prelude::*; +use super::*; +use sp_runtime::traits::Bounded; +use frame_system::RawOrigin as SystemOrigin; +use frame_benchmarking::{ + benchmarks_instance_pallet, account, whitelisted_caller, whitelist_account, impl_benchmark_test_suite +}; +use frame_support::traits::Get; +use frame_support::{traits::EnsureOrigin, dispatch::UnfilteredDispatchable}; + +use crate::Pallet as Assets; + +const SEED: u32 = 0; + +fn create_default_asset, I: 'static>(is_sufficient: bool) + -> (T::AccountId, ::Source) +{ + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let root = SystemOrigin::Root.into(); + assert!(Assets::::force_create( + root, + Default::default(), + caller_lookup.clone(), + is_sufficient, + 1u32.into(), + ).is_ok()); + (caller, caller_lookup) +} + +fn create_default_minted_asset, I: 'static>(is_sufficient: bool, amount: T::Balance) + -> (T::AccountId, ::Source) +{ + let (caller, caller_lookup) = create_default_asset::(is_sufficient); + if !is_sufficient { + T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); + } + assert!(Assets::::mint( + SystemOrigin::Signed(caller.clone()).into(), + Default::default(), + caller_lookup.clone(), + amount, + ).is_ok()); + (caller, caller_lookup) +} + +fn swap_is_sufficient, I: 'static>(s: &mut bool) { + Asset::::mutate(&T::AssetId::default(), |maybe_a| + if let Some(ref mut a) = maybe_a { sp_std::mem::swap(s, &mut a.is_sufficient) } + ); +} + +fn add_consumers, I: 'static>(minter: T::AccountId, n: u32) { + let origin = SystemOrigin::Signed(minter); + let mut s = false; + swap_is_sufficient::(&mut s); + for i in 0..n { + let target = account("consumer", i, SEED); + T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); + let target_lookup = T::Lookup::unlookup(target); + assert!(Assets::::mint(origin.clone().into(), Default::default(), target_lookup, 100u32.into()).is_ok()); + } + swap_is_sufficient::(&mut s); +} + +fn add_sufficients, I: 'static>(minter: T::AccountId, n: u32) { + let origin = SystemOrigin::Signed(minter); + let mut s = true; + swap_is_sufficient::(&mut s); + for i in 0..n { + let target = account("sufficient", i, SEED); + let target_lookup = T::Lookup::unlookup(target); + assert!(Assets::::mint(origin.clone().into(), Default::default(), target_lookup, 100u32.into()).is_ok()); + } + swap_is_sufficient::(&mut s); +} + +fn add_approvals, I: 'static>(minter: T::AccountId, n: u32) { + T::Currency::deposit_creating(&minter, T::ApprovalDeposit::get() * n.into()); + let minter_lookup = T::Lookup::unlookup(minter.clone()); + let origin = SystemOrigin::Signed(minter); + Assets::::mint( + origin.clone().into(), + Default::default(), + minter_lookup, + (100 * (n + 1)).into(), + ).unwrap(); + for i in 0..n { + let target = account("approval", i, SEED); + T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); + let target_lookup = T::Lookup::unlookup(target); + Assets::::approve_transfer( + origin.clone().into(), + Default::default(), + target_lookup, + 100u32.into(), + ).unwrap(); + } +} + +fn assert_last_event, I: 'static>(generic_event: >::Event) { + let events = frame_system::Pallet::::events(); + let system_event: ::Event = generic_event.into(); + // compare to the last event record + let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} + +fn assert_event, I: 'static>(generic_event: >::Event) { + let system_event: ::Event = generic_event.into(); + let events = frame_system::Pallet::::events(); + assert!(events.iter().any(|event_record| { + matches!(&event_record, frame_system::EventRecord { event, .. } if &system_event == event) + })); +} + +benchmarks_instance_pallet! { + create { + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, 1u32.into()) + verify { + assert_last_event::(Event::Created(Default::default(), caller.clone(), caller).into()); + } + + force_create { + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + }: _(SystemOrigin::Root, Default::default(), caller_lookup, true, 1u32.into()) + verify { + assert_last_event::(Event::ForceCreated(Default::default(), caller).into()); + } + + destroy { + let c in 0 .. 5_000; + let s in 0 .. 5_000; + let a in 0 .. 5_00; + let (caller, _) = create_default_asset::(true); + add_consumers::(caller.clone(), c); + add_sufficients::(caller.clone(), s); + add_approvals::(caller.clone(), a); + let witness = Asset::::get(T::AssetId::default()).unwrap().destroy_witness(); + }: _(SystemOrigin::Signed(caller), Default::default(), witness) + verify { + assert_last_event::(Event::Destroyed(Default::default()).into()); + } + + mint { + let (caller, caller_lookup) = create_default_asset::(true); + let amount = T::Balance::from(100u32); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, amount) + verify { + assert_last_event::(Event::Issued(Default::default(), caller, amount).into()); + } + + burn { + let amount = T::Balance::from(100u32); + let (caller, caller_lookup) = create_default_minted_asset::(true, amount); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, amount) + verify { + assert_last_event::(Event::Burned(Default::default(), caller, amount).into()); + } + + transfer { + let amount = T::Balance::from(100u32); + let (caller, caller_lookup) = create_default_minted_asset::(true, amount); + let target: T::AccountId = account("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target.clone()); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), target_lookup, amount) + verify { + assert_last_event::(Event::Transferred(Default::default(), caller, target, amount).into()); + } + + transfer_keep_alive { + let mint_amount = T::Balance::from(200u32); + let amount = T::Balance::from(100u32); + let (caller, caller_lookup) = create_default_minted_asset::(true, mint_amount); + let target: T::AccountId = account("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target.clone()); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), target_lookup, amount) + verify { + assert!(frame_system::Pallet::::account_exists(&caller)); + assert_last_event::(Event::Transferred(Default::default(), caller, target, amount).into()); + } + + force_transfer { + let amount = T::Balance::from(100u32); + let (caller, caller_lookup) = create_default_minted_asset::(true, amount); + let target: T::AccountId = account("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target.clone()); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, target_lookup, amount) + verify { + assert_last_event::( + Event::Transferred(Default::default(), caller, target, amount).into() + ); + } + + freeze { + let (caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup) + verify { + assert_last_event::(Event::Frozen(Default::default(), caller).into()); + } + + thaw { + let (caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); + Assets::::freeze( + SystemOrigin::Signed(caller.clone()).into(), + Default::default(), + caller_lookup.clone(), + )?; + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup) + verify { + assert_last_event::(Event::Thawed(Default::default(), caller).into()); + } + + freeze_asset { + let (caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); + }: _(SystemOrigin::Signed(caller.clone()), Default::default()) + verify { + assert_last_event::(Event::AssetFrozen(Default::default()).into()); + } + + thaw_asset { + let (caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); + Assets::::freeze_asset( + SystemOrigin::Signed(caller.clone()).into(), + Default::default(), + )?; + }: _(SystemOrigin::Signed(caller.clone()), Default::default()) + verify { + assert_last_event::(Event::AssetThawed(Default::default()).into()); + } + + transfer_ownership { + let (caller, _) = create_default_asset::(true); + let target: T::AccountId = account("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target.clone()); + }: _(SystemOrigin::Signed(caller), Default::default(), target_lookup) + verify { + assert_last_event::(Event::OwnerChanged(Default::default(), target).into()); + } + + set_team { + let (caller, _) = create_default_asset::(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), Default::default(), target0.clone(), target1.clone(), target2.clone()) + verify { + assert_last_event::(Event::TeamChanged( + Default::default(), + account("target", 0, SEED), + account("target", 1, SEED), + account("target", 2, SEED), + ).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 (caller, _) = create_default_asset::(true); + T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); + }: _(SystemOrigin::Signed(caller), Default::default(), name.clone(), symbol.clone(), decimals) + verify { + let id = Default::default(); + assert_last_event::(Event::MetadataSet(id, name, symbol, decimals, false).into()); + } + + clear_metadata { + let (caller, _) = create_default_asset::(true); + T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); + let dummy = vec![0u8; T::StringLimit::get() as usize]; + let origin = SystemOrigin::Signed(caller.clone()).into(); + Assets::::set_metadata(origin, Default::default(), dummy.clone(), dummy, 12)?; + }: _(SystemOrigin::Signed(caller), Default::default()) + verify { + assert_last_event::(Event::MetadataCleared(Default::default()).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; + + create_default_asset::(true); + + let origin = T::ForceOrigin::successful_origin(); + let call = Call::::force_set_metadata( + Default::default(), + name.clone(), + symbol.clone(), + decimals, + false, + ); + }: { call.dispatch_bypass_filter(origin)? } + verify { + let id = Default::default(); + assert_last_event::(Event::MetadataSet(id, name, symbol, decimals, false).into()); + } + + force_clear_metadata { + let (caller, _) = create_default_asset::(true); + T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); + let dummy = vec![0u8; T::StringLimit::get() as usize]; + let origin = SystemOrigin::Signed(caller.clone()).into(); + Assets::::set_metadata(origin, Default::default(), dummy.clone(), dummy, 12)?; + + let origin = T::ForceOrigin::successful_origin(); + let call = Call::::force_clear_metadata(Default::default()); + }: { call.dispatch_bypass_filter(origin)? } + verify { + assert_last_event::(Event::MetadataCleared(Default::default()).into()); + } + + force_asset_status { + let (caller, caller_lookup) = create_default_asset::(true); + + let origin = T::ForceOrigin::successful_origin(); + let call = Call::::force_asset_status( + Default::default(), + caller_lookup.clone(), + caller_lookup.clone(), + caller_lookup.clone(), + caller_lookup.clone(), + 100u32.into(), + true, + false, + ); + }: { call.dispatch_bypass_filter(origin)? } + verify { + assert_last_event::(Event::AssetStatusChanged(Default::default()).into()); + } + + approve_transfer { + let (caller, _) = create_default_minted_asset::(true, 100u32.into()); + T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); + + let id = Default::default(); + let delegate: T::AccountId = account("delegate", 0, SEED); + let delegate_lookup = T::Lookup::unlookup(delegate.clone()); + let amount = 100u32.into(); + }: _(SystemOrigin::Signed(caller.clone()), id, delegate_lookup, amount) + verify { + assert_last_event::(Event::ApprovedTransfer(id, caller, delegate, amount).into()); + } + + transfer_approved { + let (owner, owner_lookup) = create_default_minted_asset::(true, 100u32.into()); + T::Currency::make_free_balance_be(&owner, DepositBalanceOf::::max_value()); + + let id = Default::default(); + 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::::approve_transfer(origin, id, delegate_lookup.clone(), amount)?; + + let dest: T::AccountId = account("dest", 0, SEED); + let dest_lookup = T::Lookup::unlookup(dest.clone()); + }: _(SystemOrigin::Signed(delegate.clone()), id, owner_lookup, dest_lookup, amount) + verify { + assert!(T::Currency::reserved_balance(&owner).is_zero()); + assert_event::(Event::Transferred(id, owner, dest, amount).into()); + } + + cancel_approval { + let (caller, _) = create_default_minted_asset::(true, 100u32.into()); + T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); + + let id = Default::default(); + 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::::approve_transfer(origin, id, delegate_lookup.clone(), amount)?; + }: _(SystemOrigin::Signed(caller.clone()), id, delegate_lookup) + verify { + assert_last_event::(Event::ApprovalCancelled(id, caller, delegate).into()); + } + + force_cancel_approval { + let (caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); + T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); + + let id = Default::default(); + 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::::approve_transfer(origin, id, delegate_lookup.clone(), amount)?; + }: _(SystemOrigin::Signed(caller.clone()), id, caller_lookup, delegate_lookup) + verify { + assert_last_event::(Event::ApprovalCancelled(id, caller, delegate).into()); + } +} + +impl_benchmark_test_suite!(Assets, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/frame/uniques/src/functions.rs b/frame/uniques/src/functions.rs new file mode 100644 index 0000000000000..53162aff29794 --- /dev/null +++ b/frame/uniques/src/functions.rs @@ -0,0 +1,112 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2021 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. + +//! Functions for the Assets pallet. + +use super::*; +use sp_runtime::traits::Zero; +use sp_runtime::DispatchError; +use frame_support::traits::{PalletInfoAccess, ExistenceRequirement::AllowDeath, Get}; + +/// Conversion trait to allow a pallet instance to be converted into an account ID. +/// +/// The same account ID will be returned as long as the pallet remains with the same name and index +/// within the runtime construction. +pub trait PalletIntoAccount { + /// Convert into an account ID. This is infallible. + fn into_account() -> AccountId { Self::into_sub_account(&()) } + + /// Convert this value amalgamated with the a secondary "sub" value into an account ID. This is + /// infallible. + /// + /// NOTE: The account IDs from this and from `into_account` are *not* guaranteed to be distinct + /// for any given value of `self`, nor are different invocations to this with different types + /// `T`. For example, the following will all encode to the same account ID value: + /// - `self.into_sub_account(0u32)` + /// - `self.into_sub_account(vec![0u8; 0])` + /// - `self.into_account()` + fn into_sub_account(sub: S) -> AccountId; +} +impl PalletIntoAccount for T { + fn into_sub_account(sub: S) -> AccountId { + sp_runtime::traits::AccountIdConversion::into_sub_account(&PalletId::new::(), sub) + } +} + +#[derive(Encode, Decode)] +pub struct PalletId(u32, sp_runtime::RuntimeString); +impl sp_core::TypeId for PalletId { + const TYPE_ID: [u8; 4] = *b"PALI"; +} +impl PalletId { + pub fn new() -> Self { + Self(T::index() as u32, T::name().into()) + } +} + +// The main implementation block for the module. +impl, I: 'static> Pallet { + // Public immutables + + pub(super) fn new_instance( + owner: T::AccountId, + maybe_override_deposit: Option>, + class_details: &mut ClassDetails>, + ) -> Result>, DispatchError> { + let instances = class_details.instances.checked_add(1).ok_or(ArithmeticError::Overflow)?; + let deposit = if class_details.free_holding { + Zero::zero() + } else { + maybe_override_deposit.unwrap_or_else(T::InstanceDeposit::get) + }; + if deposit.is_zero() { + class_details.free_holds += 1; + } else { + T::Currency::transfer( + &owner, + &Self::into_account(), + deposit, + AllowDeath, + )?; + } + class_details.instances = instances; + Ok(InstanceDetails { + owner, + approved: None, + is_frozen: false, + deposit, + }) + } + + pub(super) fn dead_instance( + instance_details: &mut InstanceDetails>, + class_details: &mut ClassDetails>, + ) { + if !instance_details.deposit.is_zero() { + // Return the deposit. + let ok = T::Currency::transfer( + &Self::into_account(), + &instance_details.owner, + instance_details.deposit, + AllowDeath, + ).is_ok(); + debug_assert!(ok, "Unable to return deposited funds. Where did they go?"); + instance_details.deposit = Zero::zero(); + } + class_details.instances = class_details.instances.saturating_sub(1); + } +} diff --git a/frame/uniques/src/impl_non_fungibles.rs b/frame/uniques/src/impl_non_fungibles.rs new file mode 100644 index 0000000000000..d0ab13072a88d --- /dev/null +++ b/frame/uniques/src/impl_non_fungibles.rs @@ -0,0 +1,156 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2021 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 super::*; + +impl, I: 'static> fungibles::Inspect<::AccountId> for Pallet { + type AssetId = T::AssetId; + type Balance = T::Balance; + + fn total_issuance(asset: Self::AssetId) -> Self::Balance { + Asset::::get(asset) + .map(|x| x.supply) + .unwrap_or_else(Zero::zero) + } + + fn minimum_balance(asset: Self::AssetId) -> Self::Balance { + Asset::::get(asset) + .map(|x| x.min_balance) + .unwrap_or_else(Zero::zero) + } + + fn balance(asset: Self::AssetId, who: &::AccountId) -> Self::Balance { + Pallet::::balance(asset, who) + } + + fn reducible_balance( + asset: Self::AssetId, + who: &::AccountId, + keep_alive: bool, + ) -> Self::Balance { + Pallet::::reducible_balance(asset, who, keep_alive).unwrap_or(Zero::zero()) + } + + fn can_deposit( + asset: Self::AssetId, + who: &::AccountId, + amount: Self::Balance, + ) -> DepositConsequence { + Pallet::::can_increase(asset, who, amount) + } + + fn can_withdraw( + asset: Self::AssetId, + who: &::AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + Pallet::::can_decrease(asset, who, amount, false) + } +} + +impl, I: 'static> fungibles::Mutate<::AccountId> for Pallet { + fn mint_into( + asset: Self::AssetId, + who: &::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + Self::do_mint(asset, who, amount, None) + } + + fn burn_from( + asset: Self::AssetId, + who: &::AccountId, + amount: Self::Balance, + ) -> Result { + let f = DebitFlags { + keep_alive: false, + best_effort: false, + }; + Self::do_burn(asset, who, amount, None, f) + } + + fn slash( + asset: Self::AssetId, + who: &::AccountId, + amount: Self::Balance, + ) -> Result { + let f = DebitFlags { + keep_alive: false, + best_effort: true, + }; + Self::do_burn(asset, who, amount, None, f) + } +} + +impl, I: 'static> fungibles::Transfer for Pallet { + fn transfer( + asset: Self::AssetId, + source: &T::AccountId, + dest: &T::AccountId, + amount: T::Balance, + keep_alive: bool, + ) -> Result { + let f = TransferFlags { + keep_alive, + best_effort: false, + burn_dust: false + }; + Self::do_transfer(asset, source, dest, amount, None, f) + } +} + +impl, I: 'static> fungibles::Unbalanced for Pallet { + fn set_balance(_: Self::AssetId, _: &T::AccountId, _: Self::Balance) -> DispatchResult { + unreachable!("set_balance is not used if other functions are impl'd"); + } + fn set_total_issuance(id: T::AssetId, amount: Self::Balance) { + Asset::::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) + -> Result + { + let f = DebitFlags { keep_alive: false, best_effort: false }; + Self::decrease_balance(asset, who, amount, f, |_, _| Ok(())) + } + fn decrease_balance_at_most(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance) + -> Self::Balance + { + let f = DebitFlags { keep_alive: false, best_effort: true }; + Self::decrease_balance(asset, who, amount, f, |_, _| Ok(())) + .unwrap_or(Zero::zero()) + } + fn increase_balance(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance) + -> Result + { + Self::increase_balance(asset, who, amount, |_| Ok(()))?; + Ok(amount) + } + fn increase_balance_at_most(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance) + -> Self::Balance + { + match Self::increase_balance(asset, who, amount, |_| Ok(())) { + Ok(()) => amount, + Err(_) => Zero::zero(), + } + } +} diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs new file mode 100644 index 0000000000000..42b37d7a25d95 --- /dev/null +++ b/frame/uniques/src/lib.rs @@ -0,0 +1,1135 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2021 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. + +//! # Unique (Assets) Module +//! +//! A simple, secure module for dealing with non-fungible assets. +//! +//! ## Related Modules +//! +//! * [`System`](../frame_system/index.html) +//! * [`Support`](../frame_support/index.html) + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod weights; +/* TODO: +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +*/ +/* TODO: +#[cfg(test)] +pub mod mock; +#[cfg(test)] +mod tests; +*/ +// TODO: mod impl_non_fungibles; +mod functions; +mod types; +pub use types::*; + +use sp_std::prelude::*; +use sp_runtime::{RuntimeDebug, ArithmeticError, traits::{Zero, StaticLookup, Saturating}}; +use codec::{Encode, Decode, HasCompact}; +use frame_support::traits::{Currency, ReservableCurrency}; +use frame_system::Config as SystemConfig; + +pub use weights::WeightInfo; +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use super::*; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::config] + /// The module configuration trait. + pub trait Config: frame_system::Config { + /// The overarching event type. + type Event: From> + IsType<::Event>; + + /// Identifier for the class of asset. + type ClassId: Member + Parameter + Default + Copy + HasCompact; + + /// The type used to identify a unique asset within an asset class. + type InstanceId: Member + Parameter + Default + Copy + HasCompact; + + /// The currency mechanism, used for paying for reserves. + type Currency: ReservableCurrency; + + /// The origin which may forcibly create or destroy an asset or otherwise alter privileged + /// attributes. + type ForceOrigin: EnsureOrigin; + + /// The basic amount of funds that must be reserved for an asset class. + type ClassDeposit: Get>; + + /// The basic amount of funds that must be reserved for an asset instance. + type InstanceDeposit: Get>; + + /// The basic amount of funds that must be reserved when adding metadata to your asset. + type MetadataDepositBase: Get>; + + /// The additional funds that must be reserved for the number of bytes you store in your + /// metadata. + type MetadataDepositPerByte: Get>; + + /// The amount of funds that must be reserved when creating a new approval. + type ApprovalDeposit: Get>; + + /// The maximum length of a name or symbol stored on-chain. + type StringLimit: Get; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } + + #[pallet::storage] + /// Details of an asset class. + pub(super) type Class, I: 'static = ()> = StorageMap< + _, + Blake2_128Concat, + T::ClassId, + ClassDetails>, + >; + + #[pallet::storage] + /// The assets held by any given account; set out this way so that assets owned by a single + /// account can be enumerated. + pub(super) type Account, I: 'static = ()> = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, + Blake2_128Concat, + (T::ClassId, T::InstanceId), + (), + OptionQuery, + >; + + #[pallet::storage] + /// The assets in existence and their ownership details. + pub(super) type Asset, I: 'static = ()> = StorageDoubleMap< + _, + Blake2_128Concat, + T::ClassId, + Blake2_128Concat, + T::InstanceId, + InstanceDetails>, + OptionQuery, + >; + + #[pallet::storage] + /// Metadata of an asset class. + pub(super) type ClassMetadataOf, I: 'static = ()> = StorageMap< + _, + Blake2_128Concat, + T::ClassId, + ClassMetadata>, + ValueQuery, + >; + + #[pallet::storage] + /// Metadata of an asset instance. + pub(super) type InstanceMetadataOf, I: 'static = ()> = StorageDoubleMap< + _, + Blake2_128Concat, + T::ClassId, + Blake2_128Concat, + T::InstanceId, + InstanceMetadata>, + ValueQuery, + >; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + #[pallet::metadata( + T::AccountId = "AccountId", + T::ClassId = "AssetId", + T::InstanceId = "InstanceId", + )] + pub enum Event, I: 'static = ()> { + /// Some asset class was created. \[class, creator, owner\] + Created(T::ClassId, T::AccountId, T::AccountId), + /// Some asset class was force-created. \[asset_id, owner\] + ForceCreated(T::ClassId, T::AccountId), + /// An asset class was destroyed. + Destroyed(T::ClassId), + /// Some assets were issued. \[class, instance, owner\] + Issued(T::ClassId, T::InstanceId, T::AccountId), + /// Some assets were transferred. \[class, instance, from, to\] + Transferred(T::ClassId, T::InstanceId, T::AccountId, T::AccountId), + /// Some assets were destroyed. \[class, instance, owner\] + Burned(T::ClassId, T::InstanceId, T::AccountId), + /* + /// The management team changed \[asset_id, issuer, admin, freezer\] + TeamChanged(T::ClassId, T::AccountId, T::AccountId, T::AccountId), + /// The owner changed \[asset_id, owner\] + OwnerChanged(T::ClassId, T::AccountId), + /// Some account `who` was frozen. \[asset_id, who\] + Frozen(T::ClassId, T::AccountId), + /// Some account `who` was thawed. \[asset_id, who\] + Thawed(T::ClassId, T::AccountId), + /// Some asset `asset_id` was frozen. \[asset_id\] + AssetFrozen(T::ClassId), + /// Some asset `asset_id` was thawed. \[asset_id\] + AssetThawed(T::ClassId), + /// New metadata has been set for an asset. \[asset_id, name, symbol, decimals, is_frozen\] + MetadataSet(T::ClassId, Vec, Vec, u8, bool), + /// Metadata has been cleared for an asset. \[asset_id\] + MetadataCleared(T::ClassId), + /// (Additional) funds have been approved for transfer to a destination account. + /// \[asset_id, source, delegate, amount\] + ApprovedTransfer(T::ClassId, T::InstanceId, T::AccountId, T::AccountId), + /// An approval for account `delegate` was cancelled by `owner`. + /// \[id, owner, delegate\] + ApprovalCancelled(T::ClassId, T::AccountId, T::AccountId), + /// An `amount` was transferred in its entirety from `owner` to `destination` by + /// the approved `delegate`. + /// \[id, owner, delegate, destination\] + TransferredApproved(T::ClassId, T::InstanceId, T::AccountId, T::AccountId, T::AccountId), + /// An asset has had its attributes changed by the `Force` origin. + /// \[id\] + AssetStatusChanged(T::ClassId), + */ + } + + #[pallet::error] + pub enum Error { + /// The signing account has no permission to do the operation. + NoPermission, + /// The given asset ID is unknown. + Unknown, + /// The asset instance ID has already been used for an asset. + AlreadyExists, + /// The owner turned out to be different to what was expected. + WrongOwner, + /// Invalid witness data given. + BadWitness, + /// The asset ID is already taken. + InUse, +/* + /// The origin account is frozen. + Frozen, + /// Invalid metadata given. + BadMetadata, + /// No approval exists that would allow the transfer. + Unapproved, +*/ + } + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet {} + + #[pallet::call] + impl, I: 'static> Pallet { + /// Issue a new class of non-fungible assets from a public origin. + /// + /// This new asset class has no assets initially and its owner is the origin. + /// + /// The origin must be Signed and the sender must have sufficient funds free. + /// + /// Funds of sender are reserved by `AssetDeposit`. + /// + /// Parameters: + /// - `class`: The identifier of the new asset class. This must not be currently in use to + /// identify an existing asset. + /// - `admin`: The admin of this class of assets. The admin is the initial address of each + /// member of the asset class's admin team. + /// + /// Emits `Created` event when successful. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::create())] + pub(super) fn create( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + admin: ::Source, + ) -> DispatchResult { + let owner = ensure_signed(origin)?; + let admin = T::Lookup::lookup(admin)?; + + ensure!(!Class::::contains_key(class), Error::::InUse); + + let deposit = T::ClassDeposit::get(); + T::Currency::reserve(&owner, deposit)?; + + Class::::insert( + class, + ClassDetails { + owner: owner.clone(), + issuer: admin.clone(), + admin: admin.clone(), + freezer: admin.clone(), + deposit, + free_holding: false, + instances: 0, + free_holds: 0, + is_frozen: false, + }, + ); + Self::deposit_event(Event::Created(class, owner, admin)); + Ok(()) + } + + /// Issue a new class of non-fungible assets from a privileged origin. + /// + /// This new asset class has no assets initially. + /// + /// The origin must conform to `ForceOrigin`. + /// + /// Unlike `create`, no funds are reserved. + /// + /// - `class`: The identifier of the new asset. This must not be currently in use to identify + /// an existing asset. + /// - `owner`: The owner of this class of assets. The owner has full superuser permissions + /// over this asset, but may later change and configure the permissions using `transfer_ownership` + /// and `set_team`. + /// + /// Emits `ForceCreated` event when successful. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::force_create())] + pub(super) fn force_create( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + owner: ::Source, + free_holding: bool, + ) -> DispatchResult { + T::ForceOrigin::ensure_origin(origin)?; + let owner = T::Lookup::lookup(owner)?; + + ensure!(!Class::::contains_key(class), Error::::InUse); + + Class::::insert( + class, + ClassDetails { + owner: owner.clone(), + issuer: owner.clone(), + admin: owner.clone(), + freezer: owner.clone(), + deposit: Zero::zero(), + free_holding, + instances: 0, + free_holds: 0, + is_frozen: false, + }, + ); + Self::deposit_event(Event::ForceCreated(class, owner)); + Ok(()) + } + + /// Destroy a class of fungible assets. + /// + /// The origin must conform to `ForceOrigin` or must be `Signed` and the sender must be the + /// owner of the asset `id`. + /// + /// - `class`: The identifier of the asset to be destroyed. This must identify an existing + /// asset. + /// + /// Emits `Destroyed` event when successful. + /// + /// Weight: `O(c + p + a)` where: + /// - `c = (witness.accounts - witness.sufficients)` + /// - `s = witness.sufficients` + #[pallet::weight(T::WeightInfo::destroy( + witness.instances.saturating_sub(witness.free_holds), + witness.free_holds, + ))] + pub(super) fn destroy( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + witness: DestroyWitness, + ) -> DispatchResult { + let maybe_check_owner = match T::ForceOrigin::try_origin(origin) { + Ok(_) => None, + Err(origin) => Some(ensure_signed(origin)?), + }; + Class::::try_mutate_exists(class, |maybe_details| { + let mut class_details = maybe_details.take().ok_or(Error::::Unknown)?; + if let Some(check_owner) = maybe_check_owner { + ensure!(class_details.owner == check_owner, Error::::NoPermission); + } + ensure!(class_details.instances == witness.instances, Error::::BadWitness); + ensure!(class_details.free_holds == witness.free_holds, Error::::BadWitness); + + for (instance, mut details) in Asset::::drain_prefix(class) { + Account::::remove(&details.owner, (class, instance)); + Self::dead_instance(&mut details, &mut class_details); + } + debug_assert_eq!(class_details.instances, 0); + debug_assert_eq!(class_details.free_holds, 0); + + let metadata = ClassMetadataOf::::take(&class); + T::Currency::unreserve( + &class_details.owner, + class_details.deposit.saturating_add(metadata.deposit), + ); + + Self::deposit_event(Event::Destroyed(class)); + + // NOTE: could use postinfo to reflect the actual number of accounts/sufficient/approvals + Ok(()) + }) + } + + /// Mint an asset instance of a particular class. + /// + /// The origin must be Signed and the sender must be the Issuer of the asset `id`. + /// + /// - `class`: The class of the asset to be minted. + /// - `instance`: The instance value of the asset to be minted. + /// - `beneficiary`: The account to be credited with the minted asset. + /// + /// Emits `Issued` event when successful. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::mint())] + pub(super) fn mint( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + #[pallet::compact] instance: T::InstanceId, + beneficiary: ::Source, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let beneficiary = T::Lookup::lookup(beneficiary)?; + + ensure!(!Asset::::contains_key(class, instance), Error::::AlreadyExists); + + Class::::try_mutate(&class, |maybe_class_details| -> DispatchResult { + let class_details = maybe_class_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(class_details.issuer == origin, Error::::NoPermission); + let details = Self::new_instance(beneficiary.clone(), None, class_details)?; + Asset::::insert(&class, &instance, details); + Ok(()) + })?; + let class_instance = (class, instance); + Account::::insert(&beneficiary, &(class, instance), ()); + let (class, instance) = class_instance; + + Self::deposit_event(Event::Issued(class, instance, beneficiary)); + Ok(()) + } + + /// Destroy a single asset instance. + /// + /// Origin must be Signed and the sender should be the Manager of the asset `id`. + /// + /// - `class`: The class of the asset to be burned. + /// - `instance`: The instance of the asset to be burned. + /// - `check_owner`: If `Some` then the operation will fail with `WrongOwner` unless the + /// asset is owned by this value. + /// + /// Emits `Burned` with the actual amount burned. + /// + /// Weight: `O(1)` + /// Modes: `check_owner.is_some()`. + #[pallet::weight(T::WeightInfo::burn())] + pub(super) fn burn( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + #[pallet::compact] instance: T::InstanceId, + check_owner: Option<::Source>, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let check_owner = check_owner.map(T::Lookup::lookup).transpose()?; + + let owner = Class::::try_mutate(&class, |maybe_class_details| -> Result { + let class_details = maybe_class_details.as_mut().ok_or(Error::::Unknown)?; + let mut details = Asset::::get(&class, &instance) + .ok_or(Error::::Unknown)?; + let is_permitted = class_details.admin == origin || details.owner == origin; + ensure!(is_permitted, Error::::NoPermission); + ensure!(check_owner.map_or(true, |o| o == details.owner), Error::::WrongOwner); + Self::dead_instance(&mut details, class_details); + Ok(details.owner) + })?; + + Asset::::remove(&class, &instance); + let class_instance = (class, instance); + Account::::remove(&owner, &class_instance); + let (class, instance) = class_instance; + + Self::deposit_event(Event::Burned(class, instance, owner)); + Ok(()) + } + + /// Move an asset from the sender account to another. + /// + /// Origin must be Signed. + /// + /// - `class`: The class of the asset to be transferred. + /// - `instance`: The instance of the asset to be transferred. + /// - `dest`: The account to be credited. + /// + /// Emits `Transferred` with the asset which was transferred. + /// + /// Weight: `O(1)` + /// Modes: source == dest. + #[pallet::weight(T::WeightInfo::transfer())] + pub(super) fn transfer( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + #[pallet::compact] instance: T::InstanceId, + dest: ::Source, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let dest = T::Lookup::lookup(dest)?; + + let mut details = Asset::::get(&class, &instance).ok_or(Error::::Unknown)?; + ensure!(details.owner == origin, Error::::NoPermission); + + let class_instance = (class, instance); + Account::::remove(&origin, &(class_instance)); + Account::::insert(&dest, &(class_instance), ()); + let (class, instance) = class_instance; + details.owner = dest; + Asset::::insert(&class, &instance, &details); + + Self::deposit_event(Event::Transferred(class, instance, origin, details.owner)); + + Ok(()) + } +/* + /// Move some assets from one account to another. + /// + /// Origin must be Signed and the sender should be the Admin of the asset `id`. + /// + /// - `class`: The identifier of the asset to have some amount transferred. + /// - `source`: The account to be debited. + /// - `dest`: The account to be credited. + /// - `amount`: The amount by which the `source`'s balance of assets should be reduced and + /// `dest`'s balance increased. The amount actually transferred may be slightly greater in + /// the case that the transfer would otherwise take the `source` balance above zero but + /// below the minimum balance. Must be greater than zero. + /// + /// Emits `Transferred` with the actual amount transferred. If this takes the source balance + /// to below the minimum for the asset, then the amount transferred is increased to take it + /// to zero. + /// + /// Weight: `O(1)` + /// Modes: Pre-existence of `dest`; Post-existence of `source`; Prior & post zombie-status + /// of `source`; Account pre-existence of `dest`. + #[pallet::weight(T::WeightInfo::force_transfer())] + pub(super) fn force_transfer( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + source: ::Source, + dest: ::Source, + #[pallet::compact] amount: T::InstanceId, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let source = T::Lookup::lookup(source)?; + let dest = T::Lookup::lookup(dest)?; + + let f = TransferFlags { + keep_alive: false, + best_effort: false, + burn_dust: false + }; + Self::do_transfer(id, &source, &dest, amount, Some(origin), f).map(|_| ()) + } + + /// Disallow further unprivileged transfers from an account. + /// + /// Origin must be Signed and the sender should be the Freezer of the asset `id`. + /// + /// - `class`: The identifier of the asset to be frozen. + /// - `who`: The account to be frozen. + /// + /// Emits `Frozen`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::freeze())] + pub(super) fn freeze( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + who: ::Source + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + + let d = Asset::::get(id).ok_or(Error::::Unknown)?; + ensure!(&origin == &d.freezer, Error::::NoPermission); + let who = T::Lookup::lookup(who)?; + ensure!( + Account::::contains_key(id, &who), + Error::::BalanceZero + ); + + Account::::mutate(id, &who, |a| a.is_frozen = true); + + Self::deposit_event(Event::::Frozen(id, who)); + Ok(()) + } + + /// Allow unprivileged transfers from an account again. + /// + /// Origin must be Signed and the sender should be the Admin of the asset `id`. + /// + /// - `class`: The identifier of the asset to be frozen. + /// - `who`: The account to be unfrozen. + /// + /// Emits `Thawed`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::thaw())] + pub(super) fn thaw( + origin: OriginFor, + #[pallet::compact] + class: T::ClassId, + who: ::Source + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + + let details = Asset::::get(id).ok_or(Error::::Unknown)?; + ensure!(&origin == &details.admin, Error::::NoPermission); + let who = T::Lookup::lookup(who)?; + ensure!( + Account::::contains_key(id, &who), + Error::::BalanceZero + ); + + Account::::mutate(id, &who, |a| a.is_frozen = false); + + Self::deposit_event(Event::::Thawed(id, who)); + Ok(()) + } + + /// Disallow further unprivileged transfers for the asset class. + /// + /// Origin must be Signed and the sender should be the Freezer of the asset `id`. + /// + /// - `class`: The identifier of the asset to be frozen. + /// + /// Emits `Frozen`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::freeze_asset())] + pub(super) fn freeze_asset( + origin: OriginFor, + #[pallet::compact] class: T::ClassId + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + + Asset::::try_mutate(id, |maybe_details| { + let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &d.freezer, Error::::NoPermission); + + d.is_frozen = true; + + Self::deposit_event(Event::::AssetFrozen(id)); + Ok(()) + }) + } + + /// Allow unprivileged transfers for the asset again. + /// + /// Origin must be Signed and the sender should be the Admin of the asset `id`. + /// + /// - `class`: The identifier of the asset to be frozen. + /// + /// Emits `Thawed`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::thaw_asset())] + pub(super) fn thaw_asset( + origin: OriginFor, + #[pallet::compact] class: T::ClassId + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + + Asset::::try_mutate(id, |maybe_details| { + let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &d.admin, Error::::NoPermission); + + d.is_frozen = false; + + Self::deposit_event(Event::::AssetThawed(id)); + Ok(()) + }) + } + + /// Change the Owner of an asset. + /// + /// Origin must be Signed and the sender should be the Owner of the asset `id`. + /// + /// - `class`: The identifier of the asset. + /// - `owner`: The new Owner of this asset. + /// + /// Emits `OwnerChanged`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::transfer_ownership())] + pub(super) fn transfer_ownership( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + owner: ::Source, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let owner = T::Lookup::lookup(owner)?; + + Asset::::try_mutate(id, |maybe_details| { + let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &details.owner, Error::::NoPermission); + if details.owner == owner { + return Ok(()); + } + + let metadata_deposit = Metadata::::get(id).deposit; + let deposit = details.deposit + metadata_deposit; + + // Move the deposit to the new owner. + T::Currency::repatriate_reserved(&details.owner, &owner, deposit, Reserved)?; + + details.owner = owner.clone(); + + Self::deposit_event(Event::OwnerChanged(id, owner)); + Ok(()) + }) + } + + /// Change the Issuer, Admin and Freezer of an asset. + /// + /// Origin must be Signed and the sender should be the Owner of the asset `id`. + /// + /// - `class`: The identifier of the asset to be frozen. + /// - `issuer`: The new Issuer of this asset. + /// - `admin`: The new Admin of this asset. + /// - `freezer`: The new Freezer of this asset. + /// + /// Emits `TeamChanged`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::set_team())] + pub(super) fn set_team( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + issuer: ::Source, + admin: ::Source, + freezer: ::Source, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let issuer = T::Lookup::lookup(issuer)?; + let admin = T::Lookup::lookup(admin)?; + let freezer = T::Lookup::lookup(freezer)?; + + Asset::::try_mutate(id, |maybe_details| { + let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &details.owner, Error::::NoPermission); + + details.issuer = issuer.clone(); + details.admin = admin.clone(); + details.freezer = freezer.clone(); + + Self::deposit_event(Event::TeamChanged(id, issuer, admin, freezer)); + Ok(()) + }) + } + + /// Set the metadata for an asset. + /// + /// Origin must be Signed and the sender should be the Owner of the asset `id`. + /// + /// Funds of sender are reserved according to the formula: + /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into + /// account any already reserved funds. + /// + /// - `class`: The identifier of the asset to update. + /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`. + /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`. + /// - `decimals`: The number of decimals this asset uses to represent one unit. + /// + /// Emits `MetadataSet`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::set_metadata(name.len() as u32, symbol.len() as u32))] + pub(super) fn set_metadata( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + name: Vec, + symbol: Vec, + decimals: u8, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + + ensure!(name.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + ensure!(symbol.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + + let d = Asset::::get(id).ok_or(Error::::Unknown)?; + ensure!(&origin == &d.owner, Error::::NoPermission); + + Metadata::::try_mutate_exists(id, |metadata| { + ensure!( + metadata.as_ref().map_or(true, |m| !m.is_frozen), + Error::::NoPermission + ); + + let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); + let new_deposit = T::MetadataDepositPerByte::get() + .saturating_mul(((name.len() + symbol.len()) as u32).into()) + .saturating_add(T::MetadataDepositBase::get()); + + if new_deposit > old_deposit { + T::Currency::reserve(&origin, new_deposit - old_deposit)?; + } else { + T::Currency::unreserve(&origin, old_deposit - new_deposit); + } + + *metadata = Some(AssetMetadata { + deposit: new_deposit, + name: name.clone(), + symbol: symbol.clone(), + decimals, + is_frozen: false, + }); + + Self::deposit_event(Event::MetadataSet(id, name, symbol, decimals, false)); + Ok(()) + }) + } + + /// Clear the metadata for an asset. + /// + /// Origin must be Signed and the sender should be the Owner of the asset `id`. + /// + /// Any deposit is freed for the asset owner. + /// + /// - `class`: The identifier of the asset to clear. + /// + /// Emits `MetadataCleared`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::clear_metadata())] + pub(super) fn clear_metadata( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + + let d = Asset::::get(id).ok_or(Error::::Unknown)?; + ensure!(&origin == &d.owner, Error::::NoPermission); + + Metadata::::try_mutate_exists(id, |metadata| { + let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; + T::Currency::unreserve(&d.owner, deposit); + Self::deposit_event(Event::MetadataCleared(id)); + Ok(()) + }) + } + + /// Force the metadata for an asset to some value. + /// + /// Origin must be ForceOrigin. + /// + /// Any deposit is left alone. + /// + /// - `class`: The identifier of the asset to update. + /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`. + /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`. + /// - `decimals`: The number of decimals this asset uses to represent one unit. + /// + /// Emits `MetadataSet`. + /// + /// Weight: `O(N + S)` where N and S are the length of the name and symbol respectively. + #[pallet::weight(T::WeightInfo::force_set_metadata(name.len() as u32, symbol.len() as u32))] + pub(super) fn force_set_metadata( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + name: Vec, + symbol: Vec, + decimals: u8, + is_frozen: bool, + ) -> DispatchResult { + T::ForceOrigin::ensure_origin(origin)?; + + ensure!(name.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + ensure!(symbol.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + + ensure!(Asset::::contains_key(id), Error::::Unknown); + Metadata::::try_mutate_exists(id, |metadata| { + let deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); + *metadata = Some(AssetMetadata { + deposit, + name: name.clone(), + symbol: symbol.clone(), + decimals, + is_frozen, + }); + + Self::deposit_event(Event::MetadataSet(id, name, symbol, decimals, is_frozen)); + Ok(()) + }) + } + + /// Clear the metadata for an asset. + /// + /// Origin must be ForceOrigin. + /// + /// Any deposit is returned. + /// + /// - `class`: The identifier of the asset to clear. + /// + /// Emits `MetadataCleared`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::force_clear_metadata())] + pub(super) fn force_clear_metadata( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + ) -> DispatchResult { + T::ForceOrigin::ensure_origin(origin)?; + + let d = Asset::::get(id).ok_or(Error::::Unknown)?; + Metadata::::try_mutate_exists(id, |metadata| { + let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; + T::Currency::unreserve(&d.owner, deposit); + Self::deposit_event(Event::MetadataCleared(id)); + Ok(()) + }) + } + + /// Alter the attributes of a given asset. + /// + /// Origin must be `ForceOrigin`. + /// + /// - `class`: The identifier of the asset. + /// - `owner`: The new Owner of this asset. + /// - `issuer`: The new Issuer of this asset. + /// - `admin`: The new Admin of this asset. + /// - `freezer`: The new Freezer of this asset. + /// - `min_balance`: The minimum balance of this new asset that any single account must + /// have. If an account's balance is reduced below this, then it collapses to zero. + /// - `is_sufficient`: Whether a non-zero balance of this asset is deposit of sufficient + /// value to account for the state bloat associated with its balance storage. If set to + /// `true`, then non-zero balances may be stored without a `consumer` reference (and thus + /// an ED in the Balances pallet or whatever else is used to control user-account state + /// growth). + /// - `is_frozen`: Whether this asset class is frozen except for permissioned/admin + /// instructions. + /// + /// Emits `AssetStatusChanged` with the identity of the asset. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::force_asset_status())] + pub(super) fn force_asset_status( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + owner: ::Source, + issuer: ::Source, + admin: ::Source, + freezer: ::Source, + #[pallet::compact] min_balance: T::InstanceId, + is_sufficient: bool, + is_frozen: bool, + ) -> DispatchResult { + T::ForceOrigin::ensure_origin(origin)?; + + Asset::::try_mutate(id, |maybe_asset| { + let mut asset = maybe_asset.take().ok_or(Error::::Unknown)?; + asset.owner = T::Lookup::lookup(owner)?; + asset.issuer = T::Lookup::lookup(issuer)?; + asset.admin = T::Lookup::lookup(admin)?; + asset.freezer = T::Lookup::lookup(freezer)?; + asset.min_balance = min_balance; + asset.is_sufficient = is_sufficient; + asset.is_frozen = is_frozen; + *maybe_asset = Some(asset); + + Self::deposit_event(Event::AssetStatusChanged(id)); + Ok(()) + }) + }*/ +/* + /// Approve an amount of asset for transfer by a delegated third-party account. + /// + /// Origin must be Signed. + /// + /// Ensures that `ApprovalDeposit` worth of `Currency` is reserved from signing account + /// for the purpose of holding the approval. If some non-zero amount of assets is already + /// approved from signing account to `delegate`, then it is topped up or unreserved to + /// meet the right value. + /// + /// NOTE: The signing account does not need to own `amount` of assets at the point of + /// making this call. + /// + /// - `class`: The identifier of the asset. + /// - `delegate`: The account to delegate permission to transfer asset. + /// - `amount`: The amount of asset that may be transferred by `delegate`. If there is + /// already an approval in place, then this acts additively. + /// + /// Emits `ApprovedTransfer` on success. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::approve_transfer())] + pub(super) fn approve_transfer( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + delegate: ::Source, + #[pallet::compact] amount: T::InstanceId, + ) -> DispatchResult { + let owner = ensure_signed(origin)?; + let delegate = T::Lookup::lookup(delegate)?; + + let key = ApprovalKey { owner, delegate }; + Approvals::::try_mutate(id, &key, |maybe_approved| -> DispatchResult { + let mut approved = maybe_approved.take().unwrap_or_default(); + let deposit_required = T::ApprovalDeposit::get(); + if approved.deposit < deposit_required { + T::Currency::reserve(&key.owner, deposit_required - approved.deposit)?; + approved.deposit = deposit_required; + } + approved.amount = approved.amount.saturating_add(amount); + *maybe_approved = Some(approved); + Ok(()) + })?; + Self::deposit_event(Event::ApprovedTransfer(id, key.owner, key.delegate, amount)); + + Ok(()) + } + + /// Cancel all of some asset approved for delegated transfer by a third-party account. + /// + /// Origin must be Signed and there must be an approval in place between signer and + /// `delegate`. + /// + /// Unreserves any deposit previously reserved by `approve_transfer` for the approval. + /// + /// - `class`: The identifier of the asset. + /// - `delegate`: The account delegated permission to transfer asset. + /// + /// Emits `ApprovalCancelled` on success. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::cancel_approval())] + pub(super) fn cancel_approval( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + delegate: ::Source, + ) -> DispatchResult { + let owner = ensure_signed(origin)?; + let delegate = T::Lookup::lookup(delegate)?; + let key = ApprovalKey { owner, delegate }; + let approval = Approvals::::take(id, &key).ok_or(Error::::Unknown)?; + T::Currency::unreserve(&key.owner, approval.deposit); + + Self::deposit_event(Event::ApprovalCancelled(id, key.owner, key.delegate)); + Ok(()) + } + + /// Cancel all of some asset approved for delegated transfer by a third-party account. + /// + /// Origin must be either ForceOrigin or Signed origin with the signer being the Admin + /// account of the asset `id`. + /// + /// Unreserves any deposit previously reserved by `approve_transfer` for the approval. + /// + /// - `class`: The identifier of the asset. + /// - `delegate`: The account delegated permission to transfer asset. + /// + /// Emits `ApprovalCancelled` on success. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::force_cancel_approval())] + pub(super) fn force_cancel_approval( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + owner: ::Source, + delegate: ::Source, + ) -> DispatchResult { + T::ForceOrigin::try_origin(origin) + .map(|_| ()) + .or_else(|origin| -> DispatchResult { + let origin = ensure_signed(origin)?; + let d = Asset::::get(id).ok_or(Error::::Unknown)?; + ensure!(&origin == &d.admin, Error::::NoPermission); + Ok(()) + })?; + + let owner = T::Lookup::lookup(owner)?; + let delegate = T::Lookup::lookup(delegate)?; + + let key = ApprovalKey { owner, delegate }; + let approval = Approvals::::take(id, &key).ok_or(Error::::Unknown)?; + T::Currency::unreserve(&key.owner, approval.deposit); + + Self::deposit_event(Event::ApprovalCancelled(id, key.owner, key.delegate)); + Ok(()) + } + + /// Transfer some asset balance from a previously delegated account to some third-party + /// account. + /// + /// Origin must be Signed and there must be an approval in place by the `owner` to the + /// signer. + /// + /// If the entire amount approved for transfer is transferred, then any deposit previously + /// reserved by `approve_transfer` is unreserved. + /// + /// - `class`: The identifier of the asset. + /// - `owner`: The account which previously approved for a transfer of at least `amount` and + /// from which the asset balance will be withdrawn. + /// - `destination`: The account to which the asset balance of `amount` will be transferred. + /// - `amount`: The amount of assets to transfer. + /// + /// Emits `TransferredApproved` on success. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::transfer_approved())] + pub(super) fn transfer_approved( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + owner: ::Source, + destination: ::Source, + #[pallet::compact] amount: T::InstanceId, + ) -> DispatchResult { + let delegate = ensure_signed(origin)?; + let owner = T::Lookup::lookup(owner)?; + let destination = T::Lookup::lookup(destination)?; + + let key = ApprovalKey { owner, delegate }; + Approvals::::try_mutate_exists(id, &key, |maybe_approved| -> DispatchResult { + let mut approved = maybe_approved.take().ok_or(Error::::Unapproved)?; + let remaining = approved + .amount + .checked_sub(&amount) + .ok_or(Error::::Unapproved)?; + + let f = TransferFlags { + keep_alive: false, + best_effort: false, + burn_dust: false + }; + Self::do_transfer(id, &key.owner, &destination, amount, None, f)?; + + if remaining.is_zero() { + T::Currency::unreserve(&key.owner, approved.deposit); + } else { + approved.amount = remaining; + *maybe_approved = Some(approved); + } + Ok(()) + })?; + Ok(()) + } + */ + } +} diff --git a/frame/uniques/src/mock.rs b/frame/uniques/src/mock.rs new file mode 100644 index 0000000000000..0b7aa339835ec --- /dev/null +++ b/frame/uniques/src/mock.rs @@ -0,0 +1,148 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 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 pallet_assets; + +use sp_core::H256; +use sp_runtime::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; +use frame_support::{parameter_types, construct_runtime}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Assets: pallet_assets::{Pallet, Call, Storage, Event}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} +impl frame_system::Config for Test { + type BaseCallFilter = (); + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} + +impl pallet_balances::Config for Test { + type Balance = u64; + type DustRemoval = (); + type Event = Event; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = (); +} + +parameter_types! { + pub const AssetDeposit: u64 = 1; + pub const ApprovalDeposit: u64 = 1; + pub const StringLimit: u32 = 50; + pub const MetadataDepositBase: u64 = 1; + pub const MetadataDepositPerByte: u64 = 1; +} + +impl Config for Test { + type Event = Event; + type Balance = u64; + type AssetId = u32; + type Currency = Balances; + type ForceOrigin = frame_system::EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = StringLimit; + type Freezer = TestFreezer; + type WeightInfo = (); + type Extra = (); +} + +use std::cell::RefCell; +use std::collections::HashMap; + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub(crate) enum Hook { + Died(u32, u64), +} +thread_local! { + static FROZEN: RefCell> = RefCell::new(Default::default()); + static HOOKS: RefCell> = RefCell::new(Default::default()); +} + +pub struct TestFreezer; +impl FrozenBalance for TestFreezer { + fn frozen_balance(asset: u32, who: &u64) -> Option { + FROZEN.with(|f| f.borrow().get(&(asset, who.clone())).cloned()) + } + + fn died(asset: u32, who: &u64) { + HOOKS.with(|h| h.borrow_mut().push(Hook::Died(asset, who.clone()))); + } +} + +pub(crate) fn set_frozen_balance(asset: u32, who: u64, amount: u64) { + FROZEN.with(|f| f.borrow_mut().insert((asset, who), amount)); +} +pub(crate) fn clear_frozen_balance(asset: u32, who: u64) { + FROZEN.with(|f| f.borrow_mut().remove(&(asset, who))); +} +pub(crate) fn hooks() -> Vec { + HOOKS.with(|h| h.borrow().clone()) +} + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs new file mode 100644 index 0000000000000..f4976af023627 --- /dev/null +++ b/frame/uniques/src/tests.rs @@ -0,0 +1,634 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 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 Assets pallet. + +use super::*; +use crate::{Error, mock::*}; +use sp_runtime::TokenError; +use frame_support::{assert_ok, assert_noop, traits::Currency}; +use pallet_balances::Error as BalancesError; + +fn last_event() -> mock::Event { + frame_system::Pallet::::events().pop().expect("Event expected").event +} + +#[test] +fn basic_minting_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Assets::mint(Origin::signed(1), 0, 2, 100)); + assert_eq!(Assets::balance(0, 2), 100); + }); +} + +#[test] +fn approval_lifecycle_works() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + Balances::make_free_balance_be(&1, 1); + assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Balances::reserved_balance(&1), 1); + assert_ok!(Assets::transfer_approved(Origin::signed(2), 0, 1, 3, 40)); + assert_ok!(Assets::cancel_approval(Origin::signed(1), 0, 2)); + assert_eq!(Assets::balance(0, 1), 60); + assert_eq!(Assets::balance(0, 3), 40); + assert_eq!(Balances::reserved_balance(&1), 0); + }); +} + +#[test] +fn approval_deposits_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + let e = BalancesError::::InsufficientBalance; + assert_noop!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50), e); + + Balances::make_free_balance_be(&1, 1); + assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Balances::reserved_balance(&1), 1); + + assert_ok!(Assets::transfer_approved(Origin::signed(2), 0, 1, 3, 50)); + assert_eq!(Balances::reserved_balance(&1), 0); + + assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); + assert_ok!(Assets::cancel_approval(Origin::signed(1), 0, 2)); + assert_eq!(Balances::reserved_balance(&1), 0); + }); +} + +#[test] +fn cannot_transfer_more_than_approved() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + Balances::make_free_balance_be(&1, 1); + assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); + let e = Error::::Unapproved; + assert_noop!(Assets::transfer_approved(Origin::signed(2), 0, 1, 3, 51), e); + }); +} + +#[test] +fn cannot_transfer_more_than_exists() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + Balances::make_free_balance_be(&1, 1); + assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 101)); + let e = Error::::BalanceLow; + assert_noop!(Assets::transfer_approved(Origin::signed(2), 0, 1, 3, 101), e); + }); +} + +#[test] +fn cancel_approval_works() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + Balances::make_free_balance_be(&1, 1); + assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); + assert_noop!(Assets::cancel_approval(Origin::signed(1), 1, 2), Error::::Unknown); + assert_noop!(Assets::cancel_approval(Origin::signed(2), 0, 2), Error::::Unknown); + assert_noop!(Assets::cancel_approval(Origin::signed(1), 0, 3), Error::::Unknown); + assert_ok!(Assets::cancel_approval(Origin::signed(1), 0, 2)); + assert_noop!(Assets::cancel_approval(Origin::signed(1), 0, 2), Error::::Unknown); + }); +} + +#[test] +fn force_cancel_approval_works() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + Balances::make_free_balance_be(&1, 1); + assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); + let e = Error::::NoPermission; + assert_noop!(Assets::force_cancel_approval(Origin::signed(2), 0, 1, 2), e); + assert_noop!(Assets::force_cancel_approval(Origin::signed(1), 1, 1, 2), Error::::Unknown); + assert_noop!(Assets::force_cancel_approval(Origin::signed(1), 0, 2, 2), Error::::Unknown); + assert_noop!(Assets::force_cancel_approval(Origin::signed(1), 0, 1, 3), Error::::Unknown); + assert_ok!(Assets::force_cancel_approval(Origin::signed(1), 0, 1, 2)); + assert_noop!(Assets::force_cancel_approval(Origin::signed(1), 0, 1, 2), Error::::Unknown); + }); +} + +#[test] +fn lifecycle_should_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + assert_ok!(Assets::create(Origin::signed(1), 0, 1, 1)); + assert_eq!(Balances::reserved_balance(&1), 1); + assert!(Asset::::contains_key(0)); + + assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0], vec![0], 12)); + assert_eq!(Balances::reserved_balance(&1), 4); + assert!(Metadata::::contains_key(0)); + + Balances::make_free_balance_be(&10, 100); + assert_ok!(Assets::mint(Origin::signed(1), 0, 10, 100)); + Balances::make_free_balance_be(&20, 100); + assert_ok!(Assets::mint(Origin::signed(1), 0, 20, 100)); + assert_eq!(Account::::iter_prefix(0).count(), 2); + + let w = Asset::::get(0).unwrap().destroy_witness(); + assert_ok!(Assets::destroy(Origin::signed(1), 0, w)); + assert_eq!(Balances::reserved_balance(&1), 0); + + assert!(!Asset::::contains_key(0)); + assert!(!Metadata::::contains_key(0)); + assert_eq!(Account::::iter_prefix(0).count(), 0); + + assert_ok!(Assets::create(Origin::signed(1), 0, 1, 1)); + assert_eq!(Balances::reserved_balance(&1), 1); + assert!(Asset::::contains_key(0)); + + assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0], vec![0], 12)); + assert_eq!(Balances::reserved_balance(&1), 4); + assert!(Metadata::::contains_key(0)); + + assert_ok!(Assets::mint(Origin::signed(1), 0, 10, 100)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 20, 100)); + assert_eq!(Account::::iter_prefix(0).count(), 2); + + let w = Asset::::get(0).unwrap().destroy_witness(); + assert_ok!(Assets::destroy(Origin::root(), 0, w)); + assert_eq!(Balances::reserved_balance(&1), 0); + + assert!(!Asset::::contains_key(0)); + assert!(!Metadata::::contains_key(0)); + assert_eq!(Account::::iter_prefix(0).count(), 0); + }); +} + +#[test] +fn destroy_with_bad_witness_should_not_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + let w = Asset::::get(0).unwrap().destroy_witness(); + assert_ok!(Assets::mint(Origin::signed(1), 0, 10, 100)); + assert_noop!(Assets::destroy(Origin::signed(1), 0, w), Error::::BadWitness); + }); +} + +#[test] +fn non_providing_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, false, 1)); + + Balances::make_free_balance_be(&0, 100); + assert_ok!(Assets::mint(Origin::signed(1), 0, 0, 100)); + + // Cannot mint into account 2 since it doesn't (yet) exist... + assert_noop!(Assets::mint(Origin::signed(1), 0, 1, 100), TokenError::CannotCreate); + // ...or transfer... + assert_noop!(Assets::transfer(Origin::signed(0), 0, 1, 50), TokenError::CannotCreate); + // ...or force-transfer + assert_noop!(Assets::force_transfer(Origin::signed(1), 0, 0, 1, 50), TokenError::CannotCreate); + + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + assert_ok!(Assets::transfer(Origin::signed(0), 0, 1, 25)); + assert_ok!(Assets::force_transfer(Origin::signed(1), 0, 0, 2, 25)); + }); +} + +#[test] +fn min_balance_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 10)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Asset::::get(0).unwrap().accounts, 1); + + // Cannot create a new account with a balance that is below minimum... + assert_noop!(Assets::mint(Origin::signed(1), 0, 2, 9), TokenError::BelowMinimum); + assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 9), TokenError::BelowMinimum); + assert_noop!(Assets::force_transfer(Origin::signed(1), 0, 1, 2, 9), TokenError::BelowMinimum); + + // When deducting from an account to below minimum, it should be reaped. + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 91)); + assert!(Assets::balance(0, 1).is_zero()); + assert_eq!(Assets::balance(0, 2), 100); + assert_eq!(Asset::::get(0).unwrap().accounts, 1); + + assert_ok!(Assets::force_transfer(Origin::signed(1), 0, 2, 1, 91)); + assert!(Assets::balance(0, 2).is_zero()); + assert_eq!(Assets::balance(0, 1), 100); + assert_eq!(Asset::::get(0).unwrap().accounts, 1); + + assert_ok!(Assets::burn(Origin::signed(1), 0, 1, 91)); + assert!(Assets::balance(0, 1).is_zero()); + assert_eq!(Asset::::get(0).unwrap().accounts, 0); + }); +} + +#[test] +fn querying_total_supply_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Assets::balance(0, 1), 50); + assert_eq!(Assets::balance(0, 2), 50); + assert_ok!(Assets::transfer(Origin::signed(2), 0, 3, 31)); + assert_eq!(Assets::balance(0, 1), 50); + assert_eq!(Assets::balance(0, 2), 19); + assert_eq!(Assets::balance(0, 3), 31); + assert_ok!(Assets::burn(Origin::signed(1), 0, 3, u64::max_value())); + assert_eq!(Assets::total_supply(0), 69); + }); +} + +#[test] +fn transferring_amount_below_available_balance_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Assets::balance(0, 1), 50); + assert_eq!(Assets::balance(0, 2), 50); + }); +} + +#[test] +fn transferring_enough_to_kill_source_when_keep_alive_should_fail() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 10)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_noop!(Assets::transfer_keep_alive(Origin::signed(1), 0, 2, 91), Error::::BalanceLow); + assert_ok!(Assets::transfer_keep_alive(Origin::signed(1), 0, 2, 90)); + assert_eq!(Assets::balance(0, 1), 10); + assert_eq!(Assets::balance(0, 2), 90); + }); +} + +#[test] +fn transferring_frozen_user_should_not_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Assets::freeze(Origin::signed(1), 0, 1)); + assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 50), Error::::Frozen); + assert_ok!(Assets::thaw(Origin::signed(1), 0, 1)); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + }); +} + +#[test] +fn transferring_frozen_asset_should_not_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Assets::freeze_asset(Origin::signed(1), 0)); + assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 50), Error::::Frozen); + assert_ok!(Assets::thaw_asset(Origin::signed(1), 0)); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + }); +} + +#[test] +fn origin_guards_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_noop!(Assets::transfer_ownership(Origin::signed(2), 0, 2), Error::::NoPermission); + assert_noop!(Assets::set_team(Origin::signed(2), 0, 2, 2, 2), Error::::NoPermission); + assert_noop!(Assets::freeze(Origin::signed(2), 0, 1), Error::::NoPermission); + assert_noop!(Assets::thaw(Origin::signed(2), 0, 2), Error::::NoPermission); + assert_noop!(Assets::mint(Origin::signed(2), 0, 2, 100), Error::::NoPermission); + assert_noop!(Assets::burn(Origin::signed(2), 0, 1, 100), Error::::NoPermission); + assert_noop!(Assets::force_transfer(Origin::signed(2), 0, 1, 2, 100), Error::::NoPermission); + let w = Asset::::get(0).unwrap().destroy_witness(); + assert_noop!(Assets::destroy(Origin::signed(2), 0, w), Error::::NoPermission); + }); +} + +#[test] +fn transfer_owner_should_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + assert_ok!(Assets::create(Origin::signed(1), 0, 1, 1)); + + assert_eq!(Balances::reserved_balance(&1), 1); + + assert_ok!(Assets::transfer_ownership(Origin::signed(1), 0, 2)); + assert_eq!(Balances::reserved_balance(&2), 1); + assert_eq!(Balances::reserved_balance(&1), 0); + + assert_noop!(Assets::transfer_ownership(Origin::signed(1), 0, 1), Error::::NoPermission); + + // Set metadata now and make sure that deposit gets transferred back. + assert_ok!(Assets::set_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], 12)); + assert_ok!(Assets::transfer_ownership(Origin::signed(2), 0, 1)); + assert_eq!(Balances::reserved_balance(&1), 22); + assert_eq!(Balances::reserved_balance(&2), 0); + }); +} + +#[test] +fn set_team_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::set_team(Origin::signed(1), 0, 2, 3, 4)); + + assert_ok!(Assets::mint(Origin::signed(2), 0, 2, 100)); + assert_ok!(Assets::freeze(Origin::signed(4), 0, 2)); + assert_ok!(Assets::thaw(Origin::signed(3), 0, 2)); + assert_ok!(Assets::force_transfer(Origin::signed(3), 0, 2, 3, 100)); + assert_ok!(Assets::burn(Origin::signed(3), 0, 3, 100)); + }); +} + +#[test] +fn transferring_to_frozen_account_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 2, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_eq!(Assets::balance(0, 2), 100); + assert_ok!(Assets::freeze(Origin::signed(1), 0, 2)); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Assets::balance(0, 2), 150); + }); +} + +#[test] +fn transferring_amount_more_than_available_balance_should_not_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Assets::balance(0, 1), 50); + assert_eq!(Assets::balance(0, 2), 50); + assert_ok!(Assets::burn(Origin::signed(1), 0, 1, u64::max_value())); + assert_eq!(Assets::balance(0, 1), 0); + assert_noop!(Assets::transfer(Origin::signed(1), 0, 1, 50), Error::::BalanceLow); + assert_noop!(Assets::transfer(Origin::signed(2), 0, 1, 51), Error::::BalanceLow); + }); +} + +#[test] +fn transferring_less_than_one_unit_is_fine() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 0)); + assert_eq!( + last_event(), + mock::Event::pallet_assets(crate::Event::Transferred(0, 1, 2, 0)), + ); + }); +} + +#[test] +fn transferring_more_units_than_total_supply_should_not_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 101), Error::::BalanceLow); + }); +} + +#[test] +fn burning_asset_balance_with_positive_balance_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Assets::burn(Origin::signed(1), 0, 1, u64::max_value())); + assert_eq!(Assets::balance(0, 1), 0); + }); +} + +#[test] +fn burning_asset_balance_with_zero_balance_does_nothing() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 2), 0); + assert_ok!(Assets::burn(Origin::signed(1), 0, 2, u64::max_value())); + assert_eq!(Assets::balance(0, 2), 0); + assert_eq!(Assets::total_supply(0), 100); + }); +} + +#[test] +fn set_metadata_should_work() { + new_test_ext().execute_with(|| { + // Cannot add metadata to unknown asset + assert_noop!( + Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12), + Error::::Unknown, + ); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + // Cannot add metadata to unowned asset + assert_noop!( + Assets::set_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], 12), + Error::::NoPermission, + ); + + // Cannot add oversized metadata + assert_noop!( + Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 100], vec![0u8; 10], 12), + Error::::BadMetadata, + ); + assert_noop!( + Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 100], 12), + Error::::BadMetadata, + ); + + // Successfully add metadata and take deposit + Balances::make_free_balance_be(&1, 30); + assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12)); + assert_eq!(Balances::free_balance(&1), 9); + + // Update deposit + assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 5], 12)); + assert_eq!(Balances::free_balance(&1), 14); + assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 15], 12)); + assert_eq!(Balances::free_balance(&1), 4); + + // Cannot over-reserve + assert_noop!( + Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 20], vec![0u8; 20], 12), + BalancesError::::InsufficientBalance, + ); + + // Clear Metadata + assert!(Metadata::::contains_key(0)); + assert_noop!(Assets::clear_metadata(Origin::signed(2), 0), Error::::NoPermission); + assert_noop!(Assets::clear_metadata(Origin::signed(1), 1), Error::::Unknown); + assert_ok!(Assets::clear_metadata(Origin::signed(1), 0)); + assert!(!Metadata::::contains_key(0)); + }); +} + +#[test] +fn freezer_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 10)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + + + // freeze 50 of it. + set_frozen_balance(0, 1, 50); + + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 20)); + // cannot transfer another 21 away as this would take the non-frozen balance (30) to below + // the minimum balance (10). + assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 21), Error::::BalanceLow); + + // create an approved transfer... + Balances::make_free_balance_be(&1, 100); + assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); + let e = Error::::BalanceLow; + // ...but that wont work either: + assert_noop!(Assets::transfer_approved(Origin::signed(2), 0, 1, 2, 21), e); + // a force transfer won't work also. + let e = Error::::BalanceLow; + assert_noop!(Assets::force_transfer(Origin::signed(1), 0, 1, 2, 21), e); + + // reduce it to only 49 frozen... + set_frozen_balance(0, 1, 49); + // ...and it's all good: + assert_ok!(Assets::force_transfer(Origin::signed(1), 0, 1, 2, 21)); + + // and if we clear it, we can remove the account completely. + clear_frozen_balance(0, 1); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(hooks(), vec![Hook::Died(0, 1)]); + }); +} + +#[test] +fn imbalances_should_work() { + use frame_support::traits::tokens::fungibles::Balanced; + + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + + let imb = Assets::issue(0, 100); + assert_eq!(Assets::total_supply(0), 100); + 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!(Assets::total_supply(0), 30); + + assert!(Assets::resolve(&1, imb1).is_ok()); + assert_eq!(Assets::balance(0, 1), 30); + assert_eq!(Assets::total_supply(0), 30); + }); +} + +#[test] +fn force_metadata_should_work() { + new_test_ext().execute_with(|| { + //force set metadata works + assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Assets::force_set_metadata(Origin::root(), 0, vec![0u8; 10], vec![0u8; 10], 8, false)); + assert!(Metadata::::contains_key(0)); + + //overwrites existing metadata + let asset_original_metadata = Metadata::::get(0); + assert_ok!(Assets::force_set_metadata(Origin::root(), 0, vec![1u8; 10], vec![1u8; 10], 8, false)); + assert_ne!(Metadata::::get(0), asset_original_metadata); + + //attempt to set metadata for non-existent asset class + assert_noop!( + Assets::force_set_metadata(Origin::root(), 1, vec![0u8; 10], vec![0u8; 10], 8, false), + Error::::Unknown + ); + + //string length limit check + let limit = StringLimit::get() as usize; + assert_noop!( + Assets::force_set_metadata(Origin::root(), 0, vec![0u8; limit + 1], vec![0u8; 10], 8, false), + Error::::BadMetadata + ); + assert_noop!( + Assets::force_set_metadata(Origin::root(), 0, vec![0u8; 10], vec![0u8; limit + 1], 8, false), + Error::::BadMetadata + ); + + //force clear metadata works + assert!(Metadata::::contains_key(0)); + assert_ok!(Assets::force_clear_metadata(Origin::root(), 0)); + assert!(!Metadata::::contains_key(0)); + + //Error handles clearing non-existent asset class + assert_noop!(Assets::force_clear_metadata(Origin::root(), 1), Error::::Unknown); + }); +} + +#[test] +fn force_asset_status_should_work(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 10); + Balances::make_free_balance_be(&2, 10); + assert_ok!(Assets::create(Origin::signed(1), 0, 1, 30)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 50)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 2, 150)); + + //force asset status to change min_balance > balance + assert_ok!(Assets::force_asset_status(Origin::root(), 0, 1, 1, 1, 1, 100, true, false)); + assert_eq!(Assets::balance(0, 1), 50); + + //account can recieve assets for balance < min_balance + assert_ok!(Assets::transfer(Origin::signed(2), 0, 1, 1)); + assert_eq!(Assets::balance(0, 1), 51); + + //account on outbound transfer will cleanup for balance < min_balance + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 1)); + assert_eq!(Assets::balance(0,1), 0); + + //won't create new account with balance below min_balance + assert_noop!(Assets::transfer(Origin::signed(2), 0, 3, 50), TokenError::BelowMinimum); + + //force asset status will not execute for non-existent class + assert_noop!( + Assets::force_asset_status(Origin::root(), 1, 1, 1, 1, 1, 90, true, false), + Error::::Unknown + ); + + //account drains to completion when funds dip below min_balance + assert_ok!(Assets::force_asset_status(Origin::root(), 0, 1, 1, 1, 1, 110, true, false)); + assert_ok!(Assets::transfer(Origin::signed(2), 0, 1, 110)); + assert_eq!(Assets::balance(0, 1), 200); + assert_eq!(Assets::balance(0, 2), 0); + assert_eq!(Assets::total_supply(0), 200); + }); +} diff --git a/frame/uniques/src/types.rs b/frame/uniques/src/types.rs new file mode 100644 index 0000000000000..47301df5ed17c --- /dev/null +++ b/frame/uniques/src/types.rs @@ -0,0 +1,110 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2021 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::*; + +pub(super) type DepositBalanceOf = + <>::Currency as Currency<::AccountId>>::Balance; + +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)] +pub struct ClassDetails< + AccountId, + DepositBalance, +> { + /// Can change `owner`, `issuer`, `freezer` and `admin` accounts. + pub(super) owner: AccountId, + /// Can mint tokens. + pub(super) issuer: AccountId, + /// Can thaw tokens, force transfers and burn tokens from any account. + pub(super) admin: AccountId, + /// Can freeze tokens. + pub(super) freezer: AccountId, + /// The balance deposited for this asset. This pays for the data stored here. + pub(super) deposit: DepositBalance, + /// If `true`, then no deposit is needed to hold instances of this class. + pub(super) free_holding: bool, + /// The total number of outstanding unique instances of this asset class. + pub(super) instances: u32, + /// The number of instances of this asset class with a zero deposit. + pub(super) free_holds: u32, + /// Whether the asset is frozen for non-admin transfers. + pub(super) is_frozen: bool, +} + +/// Witness data for the destroy transactions. +#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)] +pub struct DestroyWitness { + /// The number of instances of this class of asset. + #[codec(compact)] + pub(super) instances: u32, + /// The number of instances of this asset class with a zero deposit. + #[codec(compact)] + pub(super) free_holds: u32, +} + +impl ClassDetails { + pub fn destroy_witness(&self) -> DestroyWitness { + DestroyWitness { + instances: self.instances, + free_holds: self.free_holds, + } + } +} + +/// Information concerning the ownership of a single unique asset. +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default)] +pub struct InstanceDetails { + /// The owner of this asset. + pub(super) owner: AccountId, + /// The approved transferrer of this asset, if one is set. + pub(super) approved: Option, + /// Whether the asset can be transferred or not. + pub(super) is_frozen: bool, + /// The amount held in the pallet's default account for this asset. Free-hold assets will have + /// this as zero. + pub(super) deposit: DepositBalance, +} + +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default)] +pub struct ClassMetadata { + /// The balance deposited for this metadata. + /// + /// This pays for the data stored in this struct. + pub(super) deposit: DepositBalance, + /// The user friendly name of this asset. Limited in length by `StringLimit`. + pub(super) name: Vec, + /// Whether the asset metadata may be changed by a non Force origin. + pub(super) is_frozen: bool, +} + +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default)] +pub struct InstanceMetadata { + /// The balance deposited for this metadata. + /// + /// This pays for the data stored in this struct. + pub(super) deposit: DepositBalance, + /// The user friendly name of this asset. Limited in length by `StringLimit`. + pub(super) name: Vec, + /// General information concerning this asset. Limited in length by `StringLimit`. This will + /// generally be either a JSON dump or the hash of some JSON which can be found on a + /// hash-addressable global publication system such as IPFS. + pub(super) information: Vec, + /// Whether the asset metadata may be changed by a non Force origin. + pub(super) is_frozen: bool, +} diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs new file mode 100644 index 0000000000000..8b2eb24ed45c0 --- /dev/null +++ b/frame/uniques/src/weights.rs @@ -0,0 +1,333 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 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. + +//! Autogenerated weights for pallet_assets +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 +//! DATE: 2021-03-08, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 + +// Executed Command: +// target/release/substrate +// benchmark +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_assets +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./frame/assets/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_assets. +pub trait WeightInfo { + fn create() -> Weight; + fn force_create() -> Weight; + fn destroy(c: u32, s: u32, ) -> Weight; + fn mint() -> Weight; + fn burn() -> Weight; + fn transfer() -> Weight; +/* fn transfer_keep_alive() -> Weight; + fn force_transfer() -> Weight; + fn freeze() -> Weight; + fn thaw() -> Weight; + fn freeze_asset() -> Weight; + fn thaw_asset() -> Weight; + fn transfer_ownership() -> Weight; + fn set_team() -> Weight; + fn set_metadata(n: u32, s: u32, ) -> Weight; + fn clear_metadata() -> Weight; + fn force_set_metadata(n: u32, s: u32, ) -> Weight; + fn force_clear_metadata() -> Weight; + fn force_asset_status() -> Weight; + fn approve_transfer() -> Weight; + fn transfer_approved() -> Weight; + fn cancel_approval() -> Weight; + fn force_cancel_approval() -> Weight;*/ +} + +/// Weights for pallet_assets using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn create() -> Weight { + (48_305_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_create() -> Weight { + (23_827_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn destroy(c: u32, s: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 38_000 + .saturating_add((24_232_000 as Weight).saturating_mul(c as Weight)) + // Standard Error: 38_000 + .saturating_add((30_467_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(c as Weight))) + .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(s as Weight))) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(c as Weight))) + .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(s as Weight))) + } + fn mint() -> Weight { + (46_433_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn burn() -> Weight { + (46_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn transfer() -> Weight { + (70_793_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } +/* fn transfer_keep_alive() -> Weight { + (57_453_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn force_transfer() -> Weight { + (70_968_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn freeze() -> Weight { + (34_290_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn thaw() -> Weight { + (34_419_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn freeze_asset() -> Weight { + (24_373_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn thaw_asset() -> Weight { + (24_096_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn transfer_ownership() -> Weight { + (28_566_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_team() -> Weight { + (25_297_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_metadata(_n: u32, s: u32, ) -> Weight { + (53_367_000 as Weight) + // Standard Error: 0 + .saturating_add((8_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn clear_metadata() -> Weight { + (51_721_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + (27_117_000 as Weight) + // Standard Error: 0 + .saturating_add((5_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_clear_metadata() -> Weight { + (51_598_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_asset_status() -> Weight { + (23_366_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn approve_transfer() -> Weight { + (47_906_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn transfer_approved() -> Weight { + (90_338_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) + } + fn cancel_approval() -> Weight { + (48_591_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_cancel_approval() -> Weight { + (54_879_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + }*/ +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn create() -> Weight { + (48_305_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn force_create() -> Weight { + (23_827_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn destroy(c: u32, s: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 38_000 + .saturating_add((24_232_000 as Weight).saturating_mul(c as Weight)) + // Standard Error: 38_000 + .saturating_add((30_467_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(c as Weight))) + .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(s as Weight))) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(c as Weight))) + .saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(s as Weight))) + } + fn mint() -> Weight { + (46_433_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } + fn burn() -> Weight { + (46_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } + fn transfer() -> Weight { + (70_793_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + } +/* fn transfer_keep_alive() -> Weight { + (57_453_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + } + fn force_transfer() -> Weight { + (70_968_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + } + fn freeze() -> Weight { + (34_290_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn thaw() -> Weight { + (34_419_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn freeze_asset() -> Weight { + (24_373_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn thaw_asset() -> Weight { + (24_096_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn transfer_ownership() -> Weight { + (28_566_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn set_team() -> Weight { + (25_297_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn set_metadata(_n: u32, s: u32, ) -> Weight { + (53_367_000 as Weight) + // Standard Error: 0 + .saturating_add((8_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn clear_metadata() -> Weight { + (51_721_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + (27_117_000 as Weight) + // Standard Error: 0 + .saturating_add((5_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn force_clear_metadata() -> Weight { + (51_598_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn force_asset_status() -> Weight { + (23_366_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn approve_transfer() -> Weight { + (47_906_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn transfer_approved() -> Weight { + (90_338_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(5 as Weight)) + } + fn cancel_approval() -> Weight { + (48_591_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn force_cancel_approval() -> Weight { + (54_879_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + }*/ +} From aa84878064fac81b9371a3a5a66a506143d3299f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 16 May 2021 16:05:26 +0200 Subject: [PATCH 02/54] force_transfer --- frame/uniques/src/lib.rs | 31 ++++++++++++++++++++----------- frame/uniques/src/weights.rs | 17 +++-------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 42b37d7a25d95..05030701dd02e 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -509,7 +509,7 @@ pub mod pallet { Ok(()) } -/* + /// Move some assets from one account to another. /// /// Origin must be Signed and the sender should be the Admin of the asset `id`. @@ -533,22 +533,31 @@ pub mod pallet { pub(super) fn force_transfer( origin: OriginFor, #[pallet::compact] class: T::ClassId, - source: ::Source, + #[pallet::compact] instance: T::InstanceId, dest: ::Source, - #[pallet::compact] amount: T::InstanceId, ) -> DispatchResult { let origin = ensure_signed(origin)?; - let source = T::Lookup::lookup(source)?; let dest = T::Lookup::lookup(dest)?; - let f = TransferFlags { - keep_alive: false, - best_effort: false, - burn_dust: false - }; - Self::do_transfer(id, &source, &dest, amount, Some(origin), f).map(|_| ()) - } + let mut details = Asset::::get(&class, &instance).ok_or(Error::::Unknown)?; + let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; + ensure!(class_details.admin == origin, Error::::NoPermission); + + let source = details.owner; + details.owner = dest; + let class_instance = (class, instance); + Account::::remove(&source, &(class_instance)); + Account::::insert(&details.owner, &(class_instance), ()); + let (class, instance) = class_instance; + + Asset::::insert(&class, &instance, &details); + + Self::deposit_event(Event::Transferred(class, instance, source, details.owner)); + + Ok(()) + } +/* /// Disallow further unprivileged transfers from an account. /// /// Origin must be Signed and the sender should be the Freezer of the asset `id`. diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 8b2eb24ed45c0..27c28f61a40c0 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -50,9 +50,8 @@ pub trait WeightInfo { fn mint() -> Weight; fn burn() -> Weight; fn transfer() -> Weight; -/* fn transfer_keep_alive() -> Weight; fn force_transfer() -> Weight; - fn freeze() -> Weight; +/* fn freeze() -> Weight; fn thaw() -> Weight; fn freeze_asset() -> Weight; fn thaw_asset() -> Weight; @@ -110,17 +109,12 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } -/* fn transfer_keep_alive() -> Weight { - (57_453_000 as Weight) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) - } fn force_transfer() -> Weight { (70_968_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn freeze() -> Weight { +/* fn freeze() -> Weight { (34_290_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) @@ -241,17 +235,12 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } -/* fn transfer_keep_alive() -> Weight { - (57_453_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) - } fn force_transfer() -> Weight { (70_968_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } - fn freeze() -> Weight { +/* fn freeze() -> Weight { (34_290_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) From 0c0c4cb2858ef5d0a9699a1df158f7b4a6d98c8a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 16 May 2021 17:17:43 +0200 Subject: [PATCH 03/54] freeze/thaw --- frame/assets/src/lib.rs | 2 +- frame/uniques/src/lib.rs | 147 ++++++++++++++++------------------- frame/uniques/src/weights.rs | 24 +++--- 3 files changed, 81 insertions(+), 92 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index c022a1e9e0ab4..9373cc34d2364 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -771,7 +771,7 @@ pub mod pallet { /// /// Origin must be Signed and the sender should be the Admin of the asset `id`. /// - /// - `id`: The identifier of the asset to be frozen. + /// - `id`: The identifier of the asset to be thawed. /// /// Emits `Thawed`. /// diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 05030701dd02e..345b1b0f41689 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -180,19 +180,19 @@ pub mod pallet { Transferred(T::ClassId, T::InstanceId, T::AccountId, T::AccountId), /// Some assets were destroyed. \[class, instance, owner\] Burned(T::ClassId, T::InstanceId, T::AccountId), + /// Some account `who` was frozen. \[ class, instance \] + Frozen(T::ClassId, T::InstanceId), + /// Some account `who` was thawed. \[ class, instance \] + Thawed(T::ClassId, T::InstanceId), + /// Some asset `class` was frozen. \[ class \] + ClassFrozen(T::ClassId), + /// Some asset `class` was thawed. \[ class \] + ClassThawed(T::ClassId), /* /// The management team changed \[asset_id, issuer, admin, freezer\] TeamChanged(T::ClassId, T::AccountId, T::AccountId, T::AccountId), /// The owner changed \[asset_id, owner\] OwnerChanged(T::ClassId, T::AccountId), - /// Some account `who` was frozen. \[asset_id, who\] - Frozen(T::ClassId, T::AccountId), - /// Some account `who` was thawed. \[asset_id, who\] - Thawed(T::ClassId, T::AccountId), - /// Some asset `asset_id` was frozen. \[asset_id\] - AssetFrozen(T::ClassId), - /// Some asset `asset_id` was thawed. \[asset_id\] - AssetThawed(T::ClassId), /// New metadata has been set for an asset. \[asset_id, name, symbol, decimals, is_frozen\] MetadataSet(T::ClassId, Vec, Vec, u8, bool), /// Metadata has been cleared for an asset. \[asset_id\] @@ -227,10 +227,9 @@ pub mod pallet { BadWitness, /// The asset ID is already taken. InUse, -/* - /// The origin account is frozen. + /// The asset instance or class is frozen. Frozen, - /// Invalid metadata given. +/* /// Invalid metadata given. BadMetadata, /// No approval exists that would allow the transfer. Unapproved, @@ -475,16 +474,15 @@ pub mod pallet { /// Move an asset from the sender account to another. /// - /// Origin must be Signed. + /// Origin must be Signed and the signing account must be the owner of the asset instance.. /// /// - `class`: The class of the asset to be transferred. /// - `instance`: The instance of the asset to be transferred. - /// - `dest`: The account to be credited. + /// - `dest`: The account to receive ownership of the asset. /// - /// Emits `Transferred` with the asset which was transferred. + /// Emits `Transferred`. /// /// Weight: `O(1)` - /// Modes: source == dest. #[pallet::weight(T::WeightInfo::transfer())] pub(super) fn transfer( origin: OriginFor, @@ -495,8 +493,12 @@ pub mod pallet { let origin = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; + let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; + ensure!(!class_details.is_frozen, Error::::Frozen); + let mut details = Asset::::get(&class, &instance).ok_or(Error::::Unknown)?; ensure!(details.owner == origin, Error::::NoPermission); + ensure!(!details.is_frozen, Error::::Frozen); let class_instance = (class, instance); Account::::remove(&origin, &(class_instance)); @@ -514,21 +516,13 @@ pub mod pallet { /// /// Origin must be Signed and the sender should be the Admin of the asset `id`. /// - /// - `class`: The identifier of the asset to have some amount transferred. - /// - `source`: The account to be debited. - /// - `dest`: The account to be credited. - /// - `amount`: The amount by which the `source`'s balance of assets should be reduced and - /// `dest`'s balance increased. The amount actually transferred may be slightly greater in - /// the case that the transfer would otherwise take the `source` balance above zero but - /// below the minimum balance. Must be greater than zero. + /// - `class`: The class of the asset to be transferred. + /// - `instance`: The instance of the asset to be transferred. + /// - `dest`: The account to receive ownership of the asset. /// - /// Emits `Transferred` with the actual amount transferred. If this takes the source balance - /// to below the minimum for the asset, then the amount transferred is increased to take it - /// to zero. + /// Emits `Transferred`. /// /// Weight: `O(1)` - /// Modes: Pre-existence of `dest`; Post-existence of `source`; Prior & post zombie-status - /// of `source`; Account pre-existence of `dest`. #[pallet::weight(T::WeightInfo::force_transfer())] pub(super) fn force_transfer( origin: OriginFor, @@ -557,13 +551,13 @@ pub mod pallet { Ok(()) } -/* - /// Disallow further unprivileged transfers from an account. + + /// Disallow further unprivileged transfer of an asset instance. /// - /// Origin must be Signed and the sender should be the Freezer of the asset `id`. + /// Origin must be Signed and the sender should be the Freezer of the asset `class`. /// - /// - `class`: The identifier of the asset to be frozen. - /// - `who`: The account to be frozen. + /// - `class`: The class of the asset to be frozen. + /// - `instance`: The instance of the asset to be frozen. /// /// Emits `Frozen`. /// @@ -572,30 +566,28 @@ pub mod pallet { pub(super) fn freeze( origin: OriginFor, #[pallet::compact] class: T::ClassId, - who: ::Source + #[pallet::compact] instance: T::InstanceId, ) -> DispatchResult { let origin = ensure_signed(origin)?; - let d = Asset::::get(id).ok_or(Error::::Unknown)?; - ensure!(&origin == &d.freezer, Error::::NoPermission); - let who = T::Lookup::lookup(who)?; - ensure!( - Account::::contains_key(id, &who), - Error::::BalanceZero - ); + let mut details = Asset::::get(&class, &instance) + .ok_or(Error::::Unknown)?; + let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; + ensure!(class_details.freezer == origin, Error::::NoPermission); - Account::::mutate(id, &who, |a| a.is_frozen = true); + details.is_frozen = true; + Asset::::insert(&class, &instance, &details); - Self::deposit_event(Event::::Frozen(id, who)); + Self::deposit_event(Event::::Frozen(class, instance)); Ok(()) } - /// Allow unprivileged transfers from an account again. + /// Allow unprivileged transfer of an asset instance. /// - /// Origin must be Signed and the sender should be the Admin of the asset `id`. + /// Origin must be Signed and the sender should be the Freezer of the asset `class`. /// - /// - `class`: The identifier of the asset to be frozen. - /// - `who`: The account to be unfrozen. + /// - `class`: The class of the asset to be thawed. + /// - `instance`: The instance of the asset to be thawed. /// /// Emits `Thawed`. /// @@ -603,80 +595,77 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::thaw())] pub(super) fn thaw( origin: OriginFor, - #[pallet::compact] - class: T::ClassId, - who: ::Source + #[pallet::compact] class: T::ClassId, + #[pallet::compact] instance: T::InstanceId, ) -> DispatchResult { let origin = ensure_signed(origin)?; - let details = Asset::::get(id).ok_or(Error::::Unknown)?; - ensure!(&origin == &details.admin, Error::::NoPermission); - let who = T::Lookup::lookup(who)?; - ensure!( - Account::::contains_key(id, &who), - Error::::BalanceZero - ); + let mut details = Asset::::get(&class, &instance) + .ok_or(Error::::Unknown)?; + let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; + ensure!(class_details.admin == origin, Error::::NoPermission); - Account::::mutate(id, &who, |a| a.is_frozen = false); + details.is_frozen = false; + Asset::::insert(&class, &instance, &details); - Self::deposit_event(Event::::Thawed(id, who)); + Self::deposit_event(Event::::Frozen(class, instance)); Ok(()) } /// Disallow further unprivileged transfers for the asset class. /// - /// Origin must be Signed and the sender should be the Freezer of the asset `id`. + /// Origin must be Signed and the sender should be the Freezer of the asset `class`. /// - /// - `class`: The identifier of the asset to be frozen. + /// - `class`: The asset class to be frozen. /// - /// Emits `Frozen`. + /// Emits `ClassFrozen`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::freeze_asset())] - pub(super) fn freeze_asset( + #[pallet::weight(T::WeightInfo::freeze_class())] + pub(super) fn freeze_class( origin: OriginFor, #[pallet::compact] class: T::ClassId ) -> DispatchResult { let origin = ensure_signed(origin)?; - Asset::::try_mutate(id, |maybe_details| { - let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; - ensure!(&origin == &d.freezer, Error::::NoPermission); + Class::::try_mutate(class, |maybe_details| { + let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &details.freezer, Error::::NoPermission); - d.is_frozen = true; + details.is_frozen = true; - Self::deposit_event(Event::::AssetFrozen(id)); + Self::deposit_event(Event::::ClassFrozen(class)); Ok(()) }) } /// Allow unprivileged transfers for the asset again. /// - /// Origin must be Signed and the sender should be the Admin of the asset `id`. + /// Origin must be Signed and the sender should be the Admin of the asset `class`. /// - /// - `class`: The identifier of the asset to be frozen. + /// - `class`: The class to be thawed. /// - /// Emits `Thawed`. + /// Emits `ClassThawed`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::thaw_asset())] - pub(super) fn thaw_asset( + #[pallet::weight(T::WeightInfo::thaw_class())] + pub(super) fn thaw_class( origin: OriginFor, #[pallet::compact] class: T::ClassId ) -> DispatchResult { let origin = ensure_signed(origin)?; - Asset::::try_mutate(id, |maybe_details| { - let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; - ensure!(&origin == &d.admin, Error::::NoPermission); + Class::::try_mutate(class, |maybe_details| { + let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &details.admin, Error::::NoPermission); - d.is_frozen = false; + details.is_frozen = false; - Self::deposit_event(Event::::AssetThawed(id)); + Self::deposit_event(Event::::ClassThawed(class)); Ok(()) }) } - +/* /// Change the Owner of an asset. /// /// Origin must be Signed and the sender should be the Owner of the asset `id`. diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 27c28f61a40c0..773b21529fbd3 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -51,11 +51,11 @@ pub trait WeightInfo { fn burn() -> Weight; fn transfer() -> Weight; fn force_transfer() -> Weight; -/* fn freeze() -> Weight; + fn freeze() -> Weight; fn thaw() -> Weight; - fn freeze_asset() -> Weight; - fn thaw_asset() -> Weight; - fn transfer_ownership() -> Weight; + fn freeze_class() -> Weight; + fn thaw_class() -> Weight; +/* fn transfer_ownership() -> Weight; fn set_team() -> Weight; fn set_metadata(n: u32, s: u32, ) -> Weight; fn clear_metadata() -> Weight; @@ -114,7 +114,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } -/* fn freeze() -> Weight { + fn freeze() -> Weight { (34_290_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) @@ -124,17 +124,17 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn freeze_asset() -> Weight { + fn freeze_class() -> Weight { (24_373_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn thaw_asset() -> Weight { + fn thaw_class() -> Weight { (24_096_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn transfer_ownership() -> Weight { +/* fn transfer_ownership() -> Weight { (28_566_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) @@ -240,7 +240,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } -/* fn freeze() -> Weight { + fn freeze() -> Weight { (34_290_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) @@ -250,17 +250,17 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn freeze_asset() -> Weight { + fn freeze_class() -> Weight { (24_373_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn thaw_asset() -> Weight { + fn thaw_class() -> Weight { (24_096_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn transfer_ownership() -> Weight { +/* fn transfer_ownership() -> Weight { (28_566_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) From a203c1ff809159e711e78774f4d6110f24663aff Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 16 May 2021 17:23:43 +0200 Subject: [PATCH 04/54] team management --- frame/uniques/src/lib.rs | 32 ++++++++++++++++---------------- frame/uniques/src/weights.rs | 12 ++++++------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 345b1b0f41689..16fcdfdf3d0a0 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -46,7 +46,7 @@ pub use types::*; use sp_std::prelude::*; use sp_runtime::{RuntimeDebug, ArithmeticError, traits::{Zero, StaticLookup, Saturating}}; use codec::{Encode, Decode, HasCompact}; -use frame_support::traits::{Currency, ReservableCurrency}; +use frame_support::traits::{Currency, ReservableCurrency, BalanceStatus::Reserved}; use frame_system::Config as SystemConfig; pub use weights::WeightInfo; @@ -188,11 +188,11 @@ pub mod pallet { ClassFrozen(T::ClassId), /// Some asset `class` was thawed. \[ class \] ClassThawed(T::ClassId), - /* - /// The management team changed \[asset_id, issuer, admin, freezer\] - TeamChanged(T::ClassId, T::AccountId, T::AccountId, T::AccountId), - /// The owner changed \[asset_id, owner\] + /// The owner changed \[ class, owner \] OwnerChanged(T::ClassId, T::AccountId), + /// The management team changed \[ class, issuer, admin, freezer \] + TeamChanged(T::ClassId, T::AccountId, T::AccountId, T::AccountId), + /* /// New metadata has been set for an asset. \[asset_id, name, symbol, decimals, is_frozen\] MetadataSet(T::ClassId, Vec, Vec, u8, bool), /// Metadata has been cleared for an asset. \[asset_id\] @@ -665,10 +665,10 @@ pub mod pallet { Ok(()) }) } -/* - /// Change the Owner of an asset. + + /// Change the Owner of an asset class. /// - /// Origin must be Signed and the sender should be the Owner of the asset `id`. + /// Origin must be Signed and the sender should be the Owner of the asset `class`. /// /// - `class`: The identifier of the asset. /// - `owner`: The new Owner of this asset. @@ -685,14 +685,14 @@ pub mod pallet { let origin = ensure_signed(origin)?; let owner = T::Lookup::lookup(owner)?; - Asset::::try_mutate(id, |maybe_details| { + Class::::try_mutate(class, |maybe_details| { let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; ensure!(&origin == &details.owner, Error::::NoPermission); if details.owner == owner { return Ok(()); } - let metadata_deposit = Metadata::::get(id).deposit; + let metadata_deposit = ClassMetadataOf::::get(class).deposit; let deposit = details.deposit + metadata_deposit; // Move the deposit to the new owner. @@ -700,14 +700,14 @@ pub mod pallet { details.owner = owner.clone(); - Self::deposit_event(Event::OwnerChanged(id, owner)); + Self::deposit_event(Event::OwnerChanged(class, owner)); Ok(()) }) } - /// Change the Issuer, Admin and Freezer of an asset. + /// Change the Issuer, Admin and Freezer of an asset class. /// - /// Origin must be Signed and the sender should be the Owner of the asset `id`. + /// Origin must be Signed and the sender should be the Owner of the asset `class`. /// /// - `class`: The identifier of the asset to be frozen. /// - `issuer`: The new Issuer of this asset. @@ -730,7 +730,7 @@ pub mod pallet { let admin = T::Lookup::lookup(admin)?; let freezer = T::Lookup::lookup(freezer)?; - Asset::::try_mutate(id, |maybe_details| { + Class::::try_mutate(class, |maybe_details| { let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; ensure!(&origin == &details.owner, Error::::NoPermission); @@ -738,11 +738,11 @@ pub mod pallet { details.admin = admin.clone(); details.freezer = freezer.clone(); - Self::deposit_event(Event::TeamChanged(id, issuer, admin, freezer)); + Self::deposit_event(Event::TeamChanged(class, issuer, admin, freezer)); Ok(()) }) } - +/* /// Set the metadata for an asset. /// /// Origin must be Signed and the sender should be the Owner of the asset `id`. diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 773b21529fbd3..acf636fa0716b 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -55,9 +55,9 @@ pub trait WeightInfo { fn thaw() -> Weight; fn freeze_class() -> Weight; fn thaw_class() -> Weight; -/* fn transfer_ownership() -> Weight; + fn transfer_ownership() -> Weight; fn set_team() -> Weight; - fn set_metadata(n: u32, s: u32, ) -> Weight; +/* fn set_metadata(n: u32, s: u32, ) -> Weight; fn clear_metadata() -> Weight; fn force_set_metadata(n: u32, s: u32, ) -> Weight; fn force_clear_metadata() -> Weight; @@ -134,7 +134,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } -/* fn transfer_ownership() -> Weight { + fn transfer_ownership() -> Weight { (28_566_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) @@ -144,7 +144,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn set_metadata(_n: u32, s: u32, ) -> Weight { +/* fn set_metadata(_n: u32, s: u32, ) -> Weight { (53_367_000 as Weight) // Standard Error: 0 .saturating_add((8_000 as Weight).saturating_mul(s as Weight)) @@ -260,7 +260,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } -/* fn transfer_ownership() -> Weight { + fn transfer_ownership() -> Weight { (28_566_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) @@ -270,7 +270,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn set_metadata(_n: u32, s: u32, ) -> Weight { +/* fn set_metadata(_n: u32, s: u32, ) -> Weight { (53_367_000 as Weight) // Standard Error: 0 .saturating_add((8_000 as Weight).saturating_mul(s as Weight)) From b2c20f8968430f1c2d499c9df2a32eb4b9dd52df Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 16 May 2021 17:56:56 +0200 Subject: [PATCH 05/54] approvals --- frame/uniques/src/lib.rs | 357 +++++++++++++++++------------------ frame/uniques/src/weights.rs | 90 ++++----- 2 files changed, 219 insertions(+), 228 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 16fcdfdf3d0a0..51c8471955838 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -192,21 +192,23 @@ pub mod pallet { OwnerChanged(T::ClassId, T::AccountId), /// The management team changed \[ class, issuer, admin, freezer \] TeamChanged(T::ClassId, T::AccountId, T::AccountId, T::AccountId), + /// An `instance` of an asset `class` has been approved by the `owner` for transfer by a + /// `delegate`. + /// \[ clsss, instance, owner, delegate \] + ApprovedTransfer(T::ClassId, T::InstanceId, T::AccountId, T::AccountId), + /// An approval for a `delegate` account to transfer the `instance` of an asset `class` was + /// cancelled by its `owner`. + /// \[ clsss, instance, owner, delegate \] + ApprovalCancelled(T::ClassId, T::InstanceId, T::AccountId, T::AccountId), + /// The `instance` of the asset `class` was transferred from `owner` to `destination` by + /// the approved `delegate`. + /// \[ class, instance, owner, delegate, destination \] + TransferredApproved(T::ClassId, T::InstanceId, T::AccountId, T::AccountId, T::AccountId), /* /// New metadata has been set for an asset. \[asset_id, name, symbol, decimals, is_frozen\] MetadataSet(T::ClassId, Vec, Vec, u8, bool), /// Metadata has been cleared for an asset. \[asset_id\] MetadataCleared(T::ClassId), - /// (Additional) funds have been approved for transfer to a destination account. - /// \[asset_id, source, delegate, amount\] - ApprovedTransfer(T::ClassId, T::InstanceId, T::AccountId, T::AccountId), - /// An approval for account `delegate` was cancelled by `owner`. - /// \[id, owner, delegate\] - ApprovalCancelled(T::ClassId, T::AccountId, T::AccountId), - /// An `amount` was transferred in its entirety from `owner` to `destination` by - /// the approved `delegate`. - /// \[id, owner, delegate, destination\] - TransferredApproved(T::ClassId, T::InstanceId, T::AccountId, T::AccountId, T::AccountId), /// An asset has had its attributes changed by the `Force` origin. /// \[id\] AssetStatusChanged(T::ClassId), @@ -229,6 +231,10 @@ pub mod pallet { InUse, /// The asset instance or class is frozen. Frozen, + /// The delegate turned out to be different to what was expected. + WrongDelegate, + /// There is no delegate approved. + NoDelegate, /* /// Invalid metadata given. BadMetadata, /// No approval exists that would allow the transfer. @@ -742,6 +748,164 @@ pub mod pallet { Ok(()) }) } + + /// Approve an instance to be transferred by a delegated third-party account. + /// + /// Origin must be Signed and must be the owner of the asset `instance`. + /// + /// - `class`: The class of the asset to be approved for transfer. + /// - `instance`: The instance of the asset to be approved for transfer. + /// - `delegate`: The account to delegate permission to transfer asset. + /// + /// Emits `ApprovedTransfer` on success. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::approve_transfer())] + pub(super) fn approve_transfer( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + #[pallet::compact] instance: T::InstanceId, + delegate: ::Source, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let delegate = T::Lookup::lookup(delegate)?; + + let mut details = Asset::::get(&class, &instance) + .ok_or(Error::::Unknown)?; + ensure!(details.owner == origin, Error::::NoPermission); + + details.approved = Some(delegate); + Asset::::insert(&class, &instance, &details); + + let delegate = details.approved.expect("set as Some above; qed"); + Self::deposit_event(Event::ApprovedTransfer(class, instance, origin, delegate)); + + Ok(()) + } + + /// Cancel the prior approval for the transfer of an asset by a delegate. + /// + /// Origin must be Signed and there must be an approval in place between signer and + /// `delegate`. + /// + /// - `class`: The class of the asset of whose approval will be cancelled. + /// - `instance`: The instance of the asset of whose approval will be cancelled. + /// - `maybe_check_delegate`: If `Some` will ensure that the given account is the one to + /// which permission of transfer is delegated. + /// + /// Emits `ApprovalCancelled` on success. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::cancel_approval())] + pub(super) fn cancel_approval( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + #[pallet::compact] instance: T::InstanceId, + maybe_check_delegate: Option<::Source>, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let maybe_check_delegate = maybe_check_delegate.map(T::Lookup::lookup).transpose()?; + + let mut details = Asset::::get(&class, &instance) + .ok_or(Error::::Unknown)?; + ensure!(details.owner == origin, Error::::NoPermission); + let old = details.approved.take().ok_or(Error::::NoDelegate)?; + if let Some(check_delegate) = maybe_check_delegate { + ensure!(check_delegate == old, Error::::WrongDelegate); + } + + Asset::::insert(&class, &instance, &details); + Self::deposit_event(Event::ApprovalCancelled(class, instance, origin, old)); + + Ok(()) + } + + /// Cancel the prior approval for the transfer of an asset by a delegate. + /// + /// Origin must be either ForceOrigin or Signed origin with the signer being the Admin + /// account of the asset `class`. + /// + /// - `class`: The class of the asset of whose approval will be cancelled. + /// - `instance`: The instance of the asset of whose approval will be cancelled. + /// - `maybe_check_delegate`: If `Some` will ensure that the given account is the one to + /// which permission of transfer is delegated. + /// + /// Emits `ApprovalCancelled` on success. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::force_cancel_approval())] + pub(super) fn force_cancel_approval( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + #[pallet::compact] instance: T::InstanceId, + maybe_check_delegate: Option<::Source>, + ) -> DispatchResult { + T::ForceOrigin::try_origin(origin) + .map(|_| ()) + .or_else(|origin| -> DispatchResult { + let origin = ensure_signed(origin)?; + let d = Class::::get(&class).ok_or(Error::::Unknown)?; + ensure!(&origin == &d.admin, Error::::NoPermission); + Ok(()) + })?; + + let maybe_check_delegate = maybe_check_delegate.map(T::Lookup::lookup).transpose()?; + + let mut details = Asset::::get(&class, &instance) + .ok_or(Error::::Unknown)?; + let old = details.approved.take().ok_or(Error::::NoDelegate)?; + if let Some(check_delegate) = maybe_check_delegate { + ensure!(check_delegate == old, Error::::WrongDelegate); + } + + Asset::::insert(&class, &instance, &details); + Self::deposit_event(Event::ApprovalCancelled(class, instance, details.owner, old)); + + Ok(()) + } + + /// Transfer some asset instance from a target account to a destination account by an + /// approved delegate. + /// + /// Origin must be Signed and there must be an approval in place by the `owner` to the + /// signer. + /// + /// - `class`: The class of the asset to transfer. + /// - `instance`: The instance of the asset to transfer. + /// - `dest`: The account to which ownership of the asset instance will be transferred. + /// + /// Emits `TransferredApproved` on success. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::transfer_approved())] + pub(super) fn transfer_approved( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + #[pallet::compact] instance: T::InstanceId, + dest: ::Source, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let dest = T::Lookup::lookup(dest)?; + + let mut details = Asset::::get(&class, &instance).ok_or(Error::::Unknown)?; + ensure!(!details.is_frozen, Error::::Frozen); + let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; + ensure!(!class_details.is_frozen, Error::::Frozen); + + let source = details.owner; + details.owner = dest; + + let class_instance = (class, instance); + Account::::remove(&source, &(class_instance)); + Account::::insert(&details.owner, &(class_instance), ()); + let (class, instance) = class_instance; + + Asset::::insert(&class, &instance, &details); + + Self::deposit_event(Event::TransferredApproved(class, instance, source, origin, details.owner)); + + Ok(()) + } /* /// Set the metadata for an asset. /// @@ -956,178 +1120,5 @@ pub mod pallet { Ok(()) }) }*/ -/* - /// Approve an amount of asset for transfer by a delegated third-party account. - /// - /// Origin must be Signed. - /// - /// Ensures that `ApprovalDeposit` worth of `Currency` is reserved from signing account - /// for the purpose of holding the approval. If some non-zero amount of assets is already - /// approved from signing account to `delegate`, then it is topped up or unreserved to - /// meet the right value. - /// - /// NOTE: The signing account does not need to own `amount` of assets at the point of - /// making this call. - /// - /// - `class`: The identifier of the asset. - /// - `delegate`: The account to delegate permission to transfer asset. - /// - `amount`: The amount of asset that may be transferred by `delegate`. If there is - /// already an approval in place, then this acts additively. - /// - /// Emits `ApprovedTransfer` on success. - /// - /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::approve_transfer())] - pub(super) fn approve_transfer( - origin: OriginFor, - #[pallet::compact] class: T::ClassId, - delegate: ::Source, - #[pallet::compact] amount: T::InstanceId, - ) -> DispatchResult { - let owner = ensure_signed(origin)?; - let delegate = T::Lookup::lookup(delegate)?; - - let key = ApprovalKey { owner, delegate }; - Approvals::::try_mutate(id, &key, |maybe_approved| -> DispatchResult { - let mut approved = maybe_approved.take().unwrap_or_default(); - let deposit_required = T::ApprovalDeposit::get(); - if approved.deposit < deposit_required { - T::Currency::reserve(&key.owner, deposit_required - approved.deposit)?; - approved.deposit = deposit_required; - } - approved.amount = approved.amount.saturating_add(amount); - *maybe_approved = Some(approved); - Ok(()) - })?; - Self::deposit_event(Event::ApprovedTransfer(id, key.owner, key.delegate, amount)); - - Ok(()) - } - - /// Cancel all of some asset approved for delegated transfer by a third-party account. - /// - /// Origin must be Signed and there must be an approval in place between signer and - /// `delegate`. - /// - /// Unreserves any deposit previously reserved by `approve_transfer` for the approval. - /// - /// - `class`: The identifier of the asset. - /// - `delegate`: The account delegated permission to transfer asset. - /// - /// Emits `ApprovalCancelled` on success. - /// - /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::cancel_approval())] - pub(super) fn cancel_approval( - origin: OriginFor, - #[pallet::compact] class: T::ClassId, - delegate: ::Source, - ) -> DispatchResult { - let owner = ensure_signed(origin)?; - let delegate = T::Lookup::lookup(delegate)?; - let key = ApprovalKey { owner, delegate }; - let approval = Approvals::::take(id, &key).ok_or(Error::::Unknown)?; - T::Currency::unreserve(&key.owner, approval.deposit); - - Self::deposit_event(Event::ApprovalCancelled(id, key.owner, key.delegate)); - Ok(()) - } - - /// Cancel all of some asset approved for delegated transfer by a third-party account. - /// - /// Origin must be either ForceOrigin or Signed origin with the signer being the Admin - /// account of the asset `id`. - /// - /// Unreserves any deposit previously reserved by `approve_transfer` for the approval. - /// - /// - `class`: The identifier of the asset. - /// - `delegate`: The account delegated permission to transfer asset. - /// - /// Emits `ApprovalCancelled` on success. - /// - /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::force_cancel_approval())] - pub(super) fn force_cancel_approval( - origin: OriginFor, - #[pallet::compact] class: T::ClassId, - owner: ::Source, - delegate: ::Source, - ) -> DispatchResult { - T::ForceOrigin::try_origin(origin) - .map(|_| ()) - .or_else(|origin| -> DispatchResult { - let origin = ensure_signed(origin)?; - let d = Asset::::get(id).ok_or(Error::::Unknown)?; - ensure!(&origin == &d.admin, Error::::NoPermission); - Ok(()) - })?; - - let owner = T::Lookup::lookup(owner)?; - let delegate = T::Lookup::lookup(delegate)?; - - let key = ApprovalKey { owner, delegate }; - let approval = Approvals::::take(id, &key).ok_or(Error::::Unknown)?; - T::Currency::unreserve(&key.owner, approval.deposit); - - Self::deposit_event(Event::ApprovalCancelled(id, key.owner, key.delegate)); - Ok(()) - } - - /// Transfer some asset balance from a previously delegated account to some third-party - /// account. - /// - /// Origin must be Signed and there must be an approval in place by the `owner` to the - /// signer. - /// - /// If the entire amount approved for transfer is transferred, then any deposit previously - /// reserved by `approve_transfer` is unreserved. - /// - /// - `class`: The identifier of the asset. - /// - `owner`: The account which previously approved for a transfer of at least `amount` and - /// from which the asset balance will be withdrawn. - /// - `destination`: The account to which the asset balance of `amount` will be transferred. - /// - `amount`: The amount of assets to transfer. - /// - /// Emits `TransferredApproved` on success. - /// - /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::transfer_approved())] - pub(super) fn transfer_approved( - origin: OriginFor, - #[pallet::compact] class: T::ClassId, - owner: ::Source, - destination: ::Source, - #[pallet::compact] amount: T::InstanceId, - ) -> DispatchResult { - let delegate = ensure_signed(origin)?; - let owner = T::Lookup::lookup(owner)?; - let destination = T::Lookup::lookup(destination)?; - - let key = ApprovalKey { owner, delegate }; - Approvals::::try_mutate_exists(id, &key, |maybe_approved| -> DispatchResult { - let mut approved = maybe_approved.take().ok_or(Error::::Unapproved)?; - let remaining = approved - .amount - .checked_sub(&amount) - .ok_or(Error::::Unapproved)?; - - let f = TransferFlags { - keep_alive: false, - best_effort: false, - burn_dust: false - }; - Self::do_transfer(id, &key.owner, &destination, amount, None, f)?; - - if remaining.is_zero() { - T::Currency::unreserve(&key.owner, approved.deposit); - } else { - approved.amount = remaining; - *maybe_approved = Some(approved); - } - Ok(()) - })?; - Ok(()) - } - */ } } diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index acf636fa0716b..bb22bed4cf32b 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -57,15 +57,15 @@ pub trait WeightInfo { fn thaw_class() -> Weight; fn transfer_ownership() -> Weight; fn set_team() -> Weight; + fn approve_transfer() -> Weight; + fn transfer_approved() -> Weight; + fn cancel_approval() -> Weight; + fn force_cancel_approval() -> Weight; /* fn set_metadata(n: u32, s: u32, ) -> Weight; fn clear_metadata() -> Weight; fn force_set_metadata(n: u32, s: u32, ) -> Weight; fn force_clear_metadata() -> Weight; - fn force_asset_status() -> Weight; - fn approve_transfer() -> Weight; - fn transfer_approved() -> Weight; - fn cancel_approval() -> Weight; - fn force_cancel_approval() -> Weight;*/ + fn force_asset_status() -> Weight;*/ } /// Weights for pallet_assets using the Substrate node and recommended hardware. @@ -144,6 +144,26 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } + fn approve_transfer() -> Weight { + (47_906_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn transfer_approved() -> Weight { + (90_338_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(5 as Weight)) + } + fn cancel_approval() -> Weight { + (48_591_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn force_cancel_approval() -> Weight { + (54_879_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } /* fn set_metadata(_n: u32, s: u32, ) -> Weight { (53_367_000 as Weight) // Standard Error: 0 @@ -172,26 +192,6 @@ impl WeightInfo for SubstrateWeight { (23_366_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn approve_transfer() -> Weight { - (47_906_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn transfer_approved() -> Weight { - (90_338_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().writes(5 as Weight)) - } - fn cancel_approval() -> Weight { - (48_591_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn force_cancel_approval() -> Weight { - (54_879_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) }*/ } @@ -270,6 +270,26 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } + fn approve_transfer() -> Weight { + (47_906_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn transfer_approved() -> Weight { + (90_338_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(5 as Weight)) + } + fn cancel_approval() -> Weight { + (48_591_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn force_cancel_approval() -> Weight { + (54_879_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } /* fn set_metadata(_n: u32, s: u32, ) -> Weight { (53_367_000 as Weight) // Standard Error: 0 @@ -298,25 +318,5 @@ impl WeightInfo for () { (23_366_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - fn approve_transfer() -> Weight { - (47_906_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - fn transfer_approved() -> Weight { - (90_338_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(5 as Weight)) - } - fn cancel_approval() -> Weight { - (48_591_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - fn force_cancel_approval() -> Weight { - (54_879_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) }*/ } From f98dcaffdd15981add6f11d3bd62f56a7f3fd20d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 16 May 2021 17:59:50 +0200 Subject: [PATCH 06/54] Fixes --- frame/uniques/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 51c8471955838..15ae565475e79 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -235,10 +235,10 @@ pub mod pallet { WrongDelegate, /// There is no delegate approved. NoDelegate, -/* /// Invalid metadata given. - BadMetadata, /// No approval exists that would allow the transfer. Unapproved, +/* /// Invalid metadata given. + BadMetadata, */ } @@ -891,6 +891,7 @@ pub mod pallet { ensure!(!details.is_frozen, Error::::Frozen); let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; ensure!(!class_details.is_frozen, Error::::Frozen); + ensure!(details.approved.as_ref() == Some(&origin), Error::::Unapproved); let source = details.owner; details.owner = dest; From da78f22ef85143d018a2d6b5de542b2d0b8bb597 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 16 May 2021 19:02:04 +0200 Subject: [PATCH 07/54] force_asset_status --- frame/uniques/src/lib.rs | 112 +++++++++++++++++------------------ frame/uniques/src/weights.rs | 24 ++++---- 2 files changed, 69 insertions(+), 67 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 15ae565475e79..970e68b67e170 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -204,14 +204,14 @@ pub mod pallet { /// the approved `delegate`. /// \[ class, instance, owner, delegate, destination \] TransferredApproved(T::ClassId, T::InstanceId, T::AccountId, T::AccountId, T::AccountId), + /// An asset `class` has had its attributes changed by the `Force` origin. + /// \[ class \] + AssetStatusChanged(T::ClassId), /* /// New metadata has been set for an asset. \[asset_id, name, symbol, decimals, is_frozen\] MetadataSet(T::ClassId, Vec, Vec, u8, bool), /// Metadata has been cleared for an asset. \[asset_id\] MetadataCleared(T::ClassId), - /// An asset has had its attributes changed by the `Force` origin. - /// \[id\] - AssetStatusChanged(T::ClassId), */ } @@ -907,7 +907,58 @@ pub mod pallet { Ok(()) } -/* + + /// Alter the attributes of a given asset. + /// + /// Origin must be `ForceOrigin`. + /// + /// - `class`: The identifier of the asset. + /// - `owner`: The new Owner of this asset. + /// - `issuer`: The new Issuer of this asset. + /// - `admin`: The new Admin of this asset. + /// - `freezer`: The new Freezer of this asset. + /// - `min_balance`: The minimum balance of this new asset that any single account must + /// have. If an account's balance is reduced below this, then it collapses to zero. + /// - `is_sufficient`: Whether a non-zero balance of this asset is deposit of sufficient + /// value to account for the state bloat associated with its balance storage. If set to + /// `true`, then non-zero balances may be stored without a `consumer` reference (and thus + /// an ED in the Balances pallet or whatever else is used to control user-account state + /// growth). + /// - `is_frozen`: Whether this asset class is frozen except for permissioned/admin + /// instructions. + /// + /// Emits `AssetStatusChanged` with the identity of the asset. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::force_asset_status())] + pub(super) fn force_asset_status( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + owner: ::Source, + issuer: ::Source, + admin: ::Source, + freezer: ::Source, + free_holding: bool, + is_frozen: bool, + ) -> DispatchResult { + T::ForceOrigin::ensure_origin(origin)?; + + Class::::try_mutate(class, |maybe_asset| { + let mut asset = maybe_asset.take().ok_or(Error::::Unknown)?; + asset.owner = T::Lookup::lookup(owner)?; + asset.issuer = T::Lookup::lookup(issuer)?; + asset.admin = T::Lookup::lookup(admin)?; + asset.freezer = T::Lookup::lookup(freezer)?; + asset.free_holding = free_holding; + asset.is_frozen = is_frozen; + *maybe_asset = Some(asset); + + Self::deposit_event(Event::AssetStatusChanged(class)); + Ok(()) + }) + } + + /* /// Set the metadata for an asset. /// /// Origin must be Signed and the sender should be the Owner of the asset `id`. @@ -1069,57 +1120,6 @@ pub mod pallet { Ok(()) }) } - - /// Alter the attributes of a given asset. - /// - /// Origin must be `ForceOrigin`. - /// - /// - `class`: The identifier of the asset. - /// - `owner`: The new Owner of this asset. - /// - `issuer`: The new Issuer of this asset. - /// - `admin`: The new Admin of this asset. - /// - `freezer`: The new Freezer of this asset. - /// - `min_balance`: The minimum balance of this new asset that any single account must - /// have. If an account's balance is reduced below this, then it collapses to zero. - /// - `is_sufficient`: Whether a non-zero balance of this asset is deposit of sufficient - /// value to account for the state bloat associated with its balance storage. If set to - /// `true`, then non-zero balances may be stored without a `consumer` reference (and thus - /// an ED in the Balances pallet or whatever else is used to control user-account state - /// growth). - /// - `is_frozen`: Whether this asset class is frozen except for permissioned/admin - /// instructions. - /// - /// Emits `AssetStatusChanged` with the identity of the asset. - /// - /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::force_asset_status())] - pub(super) fn force_asset_status( - origin: OriginFor, - #[pallet::compact] class: T::ClassId, - owner: ::Source, - issuer: ::Source, - admin: ::Source, - freezer: ::Source, - #[pallet::compact] min_balance: T::InstanceId, - is_sufficient: bool, - is_frozen: bool, - ) -> DispatchResult { - T::ForceOrigin::ensure_origin(origin)?; - - Asset::::try_mutate(id, |maybe_asset| { - let mut asset = maybe_asset.take().ok_or(Error::::Unknown)?; - asset.owner = T::Lookup::lookup(owner)?; - asset.issuer = T::Lookup::lookup(issuer)?; - asset.admin = T::Lookup::lookup(admin)?; - asset.freezer = T::Lookup::lookup(freezer)?; - asset.min_balance = min_balance; - asset.is_sufficient = is_sufficient; - asset.is_frozen = is_frozen; - *maybe_asset = Some(asset); - - Self::deposit_event(Event::AssetStatusChanged(id)); - Ok(()) - }) - }*/ +*/ } } diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index bb22bed4cf32b..98531beadfd78 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -61,11 +61,12 @@ pub trait WeightInfo { fn transfer_approved() -> Weight; fn cancel_approval() -> Weight; fn force_cancel_approval() -> Weight; + fn force_asset_status() -> Weight; /* fn set_metadata(n: u32, s: u32, ) -> Weight; fn clear_metadata() -> Weight; fn force_set_metadata(n: u32, s: u32, ) -> Weight; fn force_clear_metadata() -> Weight; - fn force_asset_status() -> Weight;*/ + */ } /// Weights for pallet_assets using the Substrate node and recommended hardware. @@ -164,6 +165,11 @@ impl WeightInfo for SubstrateWeight { .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } + fn force_asset_status() -> Weight { + (23_366_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } /* fn set_metadata(_n: u32, s: u32, ) -> Weight { (53_367_000 as Weight) // Standard Error: 0 @@ -187,11 +193,6 @@ impl WeightInfo for SubstrateWeight { (51_598_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn force_asset_status() -> Weight { - (23_366_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) }*/ } @@ -290,6 +291,11 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } + fn force_asset_status() -> Weight { + (23_366_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } /* fn set_metadata(_n: u32, s: u32, ) -> Weight { (53_367_000 as Weight) // Standard Error: 0 @@ -314,9 +320,5 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn force_asset_status() -> Weight { - (23_366_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - }*/ + */ } From ee5f32a7a328e845f8e110eef30b7b303b8d7350 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 17 May 2021 07:55:57 +0200 Subject: [PATCH 08/54] class_metadata --- frame/uniques/src/lib.rs | 173 +++++++++++------------------------ frame/uniques/src/weights.rs | 38 ++++---- 2 files changed, 70 insertions(+), 141 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 970e68b67e170..83c622406db82 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -207,12 +207,10 @@ pub mod pallet { /// An asset `class` has had its attributes changed by the `Force` origin. /// \[ class \] AssetStatusChanged(T::ClassId), - /* - /// New metadata has been set for an asset. \[asset_id, name, symbol, decimals, is_frozen\] - MetadataSet(T::ClassId, Vec, Vec, u8, bool), - /// Metadata has been cleared for an asset. \[asset_id\] + /// New metadata has been set for an asset. \[ asset_id, name, is_frozen \] + MetadataSet(T::ClassId, Vec, bool), + /// Metadata has been cleared for an asset. \[ asset_id \] MetadataCleared(T::ClassId), - */ } #[pallet::error] @@ -237,9 +235,8 @@ pub mod pallet { NoDelegate, /// No approval exists that would allow the transfer. Unapproved, -/* /// Invalid metadata given. + /// Invalid metadata given. BadMetadata, -*/ } #[pallet::hooks] @@ -958,10 +955,9 @@ pub mod pallet { }) } - /* - /// Set the metadata for an asset. + /// Set the metadata for an asset class. /// - /// Origin must be Signed and the sender should be the Owner of the asset `id`. + /// Origin must be Signed and the sender should be the Owner of the asset `class`. /// /// Funds of sender are reserved according to the formula: /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into @@ -969,157 +965,90 @@ pub mod pallet { /// /// - `class`: The identifier of the asset to update. /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`. - /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`. - /// - `decimals`: The number of decimals this asset uses to represent one unit. + /// - `is_frozen`: Whether the metadata should be frozen against further changes. /// /// Emits `MetadataSet`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::set_metadata(name.len() as u32, symbol.len() as u32))] - pub(super) fn set_metadata( + #[pallet::weight(T::WeightInfo::set_class_metadata(name.len() as u32))] + pub(super) fn set_class_metadata( origin: OriginFor, #[pallet::compact] class: T::ClassId, name: Vec, - symbol: Vec, - decimals: u8, + is_frozen: bool, ) -> DispatchResult { - let origin = ensure_signed(origin)?; + let maybe_check_owner = T::ForceOrigin::try_origin(origin) + .map(|_| None) + .or_else(|origin| ensure_signed(origin).map(Some))?; ensure!(name.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); - ensure!(symbol.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); - let d = Asset::::get(id).ok_or(Error::::Unknown)?; - ensure!(&origin == &d.owner, Error::::NoPermission); + if let Some(check_owner) = &maybe_check_owner { + let details = Class::::get(&class).ok_or(Error::::Unknown)?; + ensure!(check_owner == &details.owner, Error::::NoPermission); + } - Metadata::::try_mutate_exists(id, |metadata| { - ensure!( - metadata.as_ref().map_or(true, |m| !m.is_frozen), - Error::::NoPermission - ); + ClassMetadataOf::::try_mutate_exists(class, |metadata| { + ensure!(metadata.as_ref().map_or(true, |m| !m.is_frozen), Error::::Frozen); let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); - let new_deposit = T::MetadataDepositPerByte::get() - .saturating_mul(((name.len() + symbol.len()) as u32).into()) - .saturating_add(T::MetadataDepositBase::get()); - - if new_deposit > old_deposit { - T::Currency::reserve(&origin, new_deposit - old_deposit)?; + let deposit = if let Some(owner) = maybe_check_owner { + let deposit = T::MetadataDepositPerByte::get() + .saturating_mul((name.len() as u32).into()) + .saturating_add(T::MetadataDepositBase::get()); + + if deposit > old_deposit { + T::Currency::reserve(&owner, deposit - old_deposit)?; + } else { + T::Currency::unreserve(&owner, old_deposit - deposit); + } + deposit } else { - T::Currency::unreserve(&origin, old_deposit - new_deposit); - } - - *metadata = Some(AssetMetadata { - deposit: new_deposit, - name: name.clone(), - symbol: symbol.clone(), - decimals, - is_frozen: false, - }); - - Self::deposit_event(Event::MetadataSet(id, name, symbol, decimals, false)); - Ok(()) - }) - } - - /// Clear the metadata for an asset. - /// - /// Origin must be Signed and the sender should be the Owner of the asset `id`. - /// - /// Any deposit is freed for the asset owner. - /// - /// - `class`: The identifier of the asset to clear. - /// - /// Emits `MetadataCleared`. - /// - /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::clear_metadata())] - pub(super) fn clear_metadata( - origin: OriginFor, - #[pallet::compact] class: T::ClassId, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; + old_deposit + }; - let d = Asset::::get(id).ok_or(Error::::Unknown)?; - ensure!(&origin == &d.owner, Error::::NoPermission); - - Metadata::::try_mutate_exists(id, |metadata| { - let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; - T::Currency::unreserve(&d.owner, deposit); - Self::deposit_event(Event::MetadataCleared(id)); - Ok(()) - }) - } - - /// Force the metadata for an asset to some value. - /// - /// Origin must be ForceOrigin. - /// - /// Any deposit is left alone. - /// - /// - `class`: The identifier of the asset to update. - /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`. - /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`. - /// - `decimals`: The number of decimals this asset uses to represent one unit. - /// - /// Emits `MetadataSet`. - /// - /// Weight: `O(N + S)` where N and S are the length of the name and symbol respectively. - #[pallet::weight(T::WeightInfo::force_set_metadata(name.len() as u32, symbol.len() as u32))] - pub(super) fn force_set_metadata( - origin: OriginFor, - #[pallet::compact] class: T::ClassId, - name: Vec, - symbol: Vec, - decimals: u8, - is_frozen: bool, - ) -> DispatchResult { - T::ForceOrigin::ensure_origin(origin)?; - - ensure!(name.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); - ensure!(symbol.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); - - ensure!(Asset::::contains_key(id), Error::::Unknown); - Metadata::::try_mutate_exists(id, |metadata| { - let deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); - *metadata = Some(AssetMetadata { + *metadata = Some(ClassMetadata { deposit, name: name.clone(), - symbol: symbol.clone(), - decimals, is_frozen, }); - Self::deposit_event(Event::MetadataSet(id, name, symbol, decimals, is_frozen)); + Self::deposit_event(Event::MetadataSet(class, name, false)); Ok(()) }) } - /// Clear the metadata for an asset. + /// Clear the metadata for an asset class. /// - /// Origin must be ForceOrigin. + /// Origin must be Signed and the sender should be the Owner of the asset `class`. /// - /// Any deposit is returned. + /// Any deposit is freed for the asset class owner. /// - /// - `class`: The identifier of the asset to clear. + /// - `class`: The identifier of the asset class whose metadata to clear. /// /// Emits `MetadataCleared`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::force_clear_metadata())] - pub(super) fn force_clear_metadata( + #[pallet::weight(T::WeightInfo::clear_class_metadata())] + pub(super) fn clear_class_metadata( origin: OriginFor, #[pallet::compact] class: T::ClassId, ) -> DispatchResult { - T::ForceOrigin::ensure_origin(origin)?; + let maybe_check_owner = T::ForceOrigin::try_origin(origin) + .map(|_| None) + .or_else(|origin| ensure_signed(origin).map(Some))?; - let d = Asset::::get(id).ok_or(Error::::Unknown)?; - Metadata::::try_mutate_exists(id, |metadata| { + let details = Class::::get(&class).ok_or(Error::::Unknown)?; + if let Some(check_owner) = &maybe_check_owner { + ensure!(check_owner == &details.owner, Error::::NoPermission); + } + + ClassMetadataOf::::try_mutate_exists(class, |metadata| { let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; - T::Currency::unreserve(&d.owner, deposit); - Self::deposit_event(Event::MetadataCleared(id)); + T::Currency::unreserve(&details.owner, deposit); + Self::deposit_event(Event::MetadataCleared(class)); Ok(()) }) } -*/ } } diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 98531beadfd78..383092c13610c 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -62,11 +62,10 @@ pub trait WeightInfo { fn cancel_approval() -> Weight; fn force_cancel_approval() -> Weight; fn force_asset_status() -> Weight; -/* fn set_metadata(n: u32, s: u32, ) -> Weight; - fn clear_metadata() -> Weight; - fn force_set_metadata(n: u32, s: u32, ) -> Weight; - fn force_clear_metadata() -> Weight; - */ + fn set_class_metadata(n: u32, ) -> Weight; + fn clear_class_metadata() -> Weight; + fn set_instance_metadata(n: u32, i: u32, ) -> Weight; + fn clear_instance_metadata() -> Weight; } /// Weights for pallet_assets using the Substrate node and recommended hardware. @@ -170,30 +169,31 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } -/* fn set_metadata(_n: u32, s: u32, ) -> Weight { + fn set_class_metadata(n: u32, ) -> Weight { (53_367_000 as Weight) // Standard Error: 0 - .saturating_add((8_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn clear_metadata() -> Weight { + fn clear_class_metadata() -> Weight { (51_721_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + fn set_instance_metadata(n: u32, i: u32, ) -> Weight { (27_117_000 as Weight) // Standard Error: 0 - .saturating_add((5_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn force_clear_metadata() -> Weight { + fn clear_instance_metadata() -> Weight { (51_598_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) - }*/ + } } // For backwards compatibility and tests @@ -296,29 +296,29 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } -/* fn set_metadata(_n: u32, s: u32, ) -> Weight { + fn set_class_metadata(n: u32, ) -> Weight { (53_367_000 as Weight) // Standard Error: 0 - .saturating_add((8_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn clear_metadata() -> Weight { + fn clear_class_metadata() -> Weight { (51_721_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + fn set_instance_metadata(n: u32, i: u32, ) -> Weight { (27_117_000 as Weight) // Standard Error: 0 - .saturating_add((5_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn force_clear_metadata() -> Weight { + fn clear_instance_metadata() -> Weight { (51_598_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - */ } From c148760e4c5f35fe6876687c1aac37d38cc9f427 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 17 May 2021 08:09:51 +0200 Subject: [PATCH 09/54] instance metadata --- frame/uniques/src/lib.rs | 144 ++++++++++++++++++++++++++++++++--- frame/uniques/src/weights.rs | 48 ++++++------ 2 files changed, 157 insertions(+), 35 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 83c622406db82..24657d3881fb9 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -207,9 +207,13 @@ pub mod pallet { /// An asset `class` has had its attributes changed by the `Force` origin. /// \[ class \] AssetStatusChanged(T::ClassId), - /// New metadata has been set for an asset. \[ asset_id, name, is_frozen \] - MetadataSet(T::ClassId, Vec, bool), - /// Metadata has been cleared for an asset. \[ asset_id \] + /// New metadata has been set for an asset class. \[ asset_id, name, is_frozen \] + ClassMetadataSet(T::ClassId, Vec, bool), + /// Metadata has been cleared for an asset class. \[ asset_id \] + ClassMetadataCleared(T::ClassId), + /// New metadata has been set for an asset instance. \[ asset_id, name, info, is_frozen \] + MetadataSet(T::ClassId, Vec, Vec, bool), + /// Metadata has been cleared for an asset instance. \[ asset_id \] MetadataCleared(T::ClassId), } @@ -955,19 +959,132 @@ pub mod pallet { }) } + /// Set the metadata for an asset instance. + /// + /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the + /// asset `instance`. + /// + /// Funds of sender are reserved according to the formula: + /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into + /// account any already reserved funds. + /// + /// - `class`: The identifier of the asset class whose instance's metadata to clear. + /// - `instance`: The identifier of the asset instance whose metadata to clear. + /// - `name`: The user visible name of this asset. Limited in length by `StringLimit`. + /// - `name`: The general information of this asset. Limited in length by `StringLimit`. + /// - `is_frozen`: Whether the metadata should be frozen against further changes. + /// + /// Emits `MetadataSet`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::set_metadata(name.len() as u32, info.len() as u32))] + pub(super) fn set_metadata( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + #[pallet::compact] instance: T::InstanceId, + name: Vec, + info: Vec, + is_frozen: bool, + ) -> DispatchResult { + let maybe_check_owner = T::ForceOrigin::try_origin(origin) + .map(|_| None) + .or_else(|origin| ensure_signed(origin).map(Some))?; + + ensure!(name.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + ensure!(info.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + + if let Some(check_owner) = &maybe_check_owner { + let details = Asset::::get(&class, &instance) + .ok_or(Error::::Unknown)?; + ensure!(check_owner == &details.owner, Error::::NoPermission); + } + + InstanceMetadataOf::::try_mutate_exists(class, instance, |metadata| { + let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); + ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); + + let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); + let deposit = if let Some(owner) = maybe_check_owner { + let deposit = T::MetadataDepositPerByte::get() + .saturating_mul((name.len() as u32).into()) + .saturating_add(T::MetadataDepositBase::get()); + + if deposit > old_deposit { + T::Currency::reserve(&owner, deposit - old_deposit)?; + } else { + T::Currency::unreserve(&owner, old_deposit - deposit); + } + deposit + } else { + old_deposit + }; + + *metadata = Some(InstanceMetadata { + deposit, + name: name.clone(), + information: info.clone(), + is_frozen, + }); + + Self::deposit_event(Event::MetadataSet(class, name, info, false)); + Ok(()) + }) + } + + /// Clear the metadata for an asset instance. + /// + /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the + /// asset `instance`. + /// + /// Any deposit is freed for the asset class owner. + /// + /// - `class`: The identifier of the asset class whose instance's metadata to clear. + /// - `instance`: The identifier of the asset instance whose metadata to clear. + /// + /// Emits `MetadataCleared`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::clear_metadata())] + pub(super) fn clear_metadata( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + #[pallet::compact] instance: T::InstanceId, + ) -> DispatchResult { + let maybe_check_owner = T::ForceOrigin::try_origin(origin) + .map(|_| None) + .or_else(|origin| ensure_signed(origin).map(Some))?; + + let details = Asset::::get(&class, &instance) + .ok_or(Error::::Unknown)?; + if let Some(check_owner) = &maybe_check_owner { + ensure!(check_owner == &details.owner, Error::::NoPermission); + } + + InstanceMetadataOf::::try_mutate_exists(class, instance, |metadata| { + let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); + ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); + + let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; + T::Currency::unreserve(&details.owner, deposit); + Self::deposit_event(Event::MetadataCleared(class)); + Ok(()) + }) + } + /// Set the metadata for an asset class. /// - /// Origin must be Signed and the sender should be the Owner of the asset `class`. + /// Origin must be either `ForceOrigin` or `Signed` and the sender should be the Owner of + /// the asset `class`. /// /// Funds of sender are reserved according to the formula: /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into /// account any already reserved funds. /// /// - `class`: The identifier of the asset to update. - /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`. + /// - `name`: The user visible name of this asset. Limited in length by `StringLimit`. /// - `is_frozen`: Whether the metadata should be frozen against further changes. /// - /// Emits `MetadataSet`. + /// Emits `ClassMetadataSet`. /// /// Weight: `O(1)` #[pallet::weight(T::WeightInfo::set_class_metadata(name.len() as u32))] @@ -989,7 +1106,8 @@ pub mod pallet { } ClassMetadataOf::::try_mutate_exists(class, |metadata| { - ensure!(metadata.as_ref().map_or(true, |m| !m.is_frozen), Error::::Frozen); + let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); + ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); let deposit = if let Some(owner) = maybe_check_owner { @@ -1013,20 +1131,21 @@ pub mod pallet { is_frozen, }); - Self::deposit_event(Event::MetadataSet(class, name, false)); + Self::deposit_event(Event::ClassMetadataSet(class, name, false)); Ok(()) }) } /// Clear the metadata for an asset class. /// - /// Origin must be Signed and the sender should be the Owner of the asset `class`. + /// Origin must be either `ForceOrigin` or `Signed` and the sender should be the Owner of + /// the asset `class`. /// /// Any deposit is freed for the asset class owner. /// /// - `class`: The identifier of the asset class whose metadata to clear. /// - /// Emits `MetadataCleared`. + /// Emits `ClassMetadataCleared`. /// /// Weight: `O(1)` #[pallet::weight(T::WeightInfo::clear_class_metadata())] @@ -1044,9 +1163,12 @@ pub mod pallet { } ClassMetadataOf::::try_mutate_exists(class, |metadata| { + let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); + ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); + let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; T::Currency::unreserve(&details.owner, deposit); - Self::deposit_event(Event::MetadataCleared(class)); + Self::deposit_event(Event::ClassMetadataCleared(class)); Ok(()) }) } diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 383092c13610c..bb4fa1a496b2a 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -62,10 +62,10 @@ pub trait WeightInfo { fn cancel_approval() -> Weight; fn force_cancel_approval() -> Weight; fn force_asset_status() -> Weight; + fn set_metadata(n: u32, i: u32, ) -> Weight; + fn clear_metadata() -> Weight; fn set_class_metadata(n: u32, ) -> Weight; fn clear_class_metadata() -> Weight; - fn set_instance_metadata(n: u32, i: u32, ) -> Weight; - fn clear_instance_metadata() -> Weight; } /// Weights for pallet_assets using the Substrate node and recommended hardware. @@ -169,28 +169,28 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn set_class_metadata(n: u32, ) -> Weight { - (53_367_000 as Weight) + fn set_metadata(n: u32, i: u32, ) -> Weight { + (27_117_000 as Weight) // Standard Error: 0 - .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn clear_class_metadata() -> Weight { - (51_721_000 as Weight) + fn clear_metadata() -> Weight { + (51_598_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn set_instance_metadata(n: u32, i: u32, ) -> Weight { - (27_117_000 as Weight) + fn set_class_metadata(n: u32, ) -> Weight { + (53_367_000 as Weight) // Standard Error: 0 - .saturating_add((5_000 as Weight).saturating_mul(n as Weight)) - .saturating_add((5_000 as Weight).saturating_mul(i as Weight)) + .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn clear_instance_metadata() -> Weight { - (51_598_000 as Weight) + fn clear_class_metadata() -> Weight { + (51_721_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -296,28 +296,28 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn set_class_metadata(n: u32, ) -> Weight { - (53_367_000 as Weight) + fn set_metadata(n: u32, i: u32, ) -> Weight { + (27_117_000 as Weight) // Standard Error: 0 - .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn clear_class_metadata() -> Weight { - (51_721_000 as Weight) + fn clear_metadata() -> Weight { + (51_598_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn set_instance_metadata(n: u32, i: u32, ) -> Weight { - (27_117_000 as Weight) + fn set_class_metadata(n: u32, ) -> Weight { + (53_367_000 as Weight) // Standard Error: 0 - .saturating_add((5_000 as Weight).saturating_mul(n as Weight)) - .saturating_add((5_000 as Weight).saturating_mul(i as Weight)) + .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn clear_instance_metadata() -> Weight { - (51_598_000 as Weight) + fn clear_class_metadata() -> Weight { + (51_721_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } From ec47648a26051602c018cf582c78ff96a56a5886 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 17 May 2021 08:19:29 +0200 Subject: [PATCH 10/54] Fixes --- frame/uniques/src/lib.rs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 24657d3881fb9..b02d41b10b3f1 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -428,9 +428,7 @@ pub mod pallet { Asset::::insert(&class, &instance, details); Ok(()) })?; - let class_instance = (class, instance); - Account::::insert(&beneficiary, &(class, instance), ()); - let (class, instance) = class_instance; + Account::::insert(&beneficiary, (&class, &instance), ()); Self::deposit_event(Event::Issued(class, instance, beneficiary)); Ok(()) @@ -471,9 +469,7 @@ pub mod pallet { })?; Asset::::remove(&class, &instance); - let class_instance = (class, instance); - Account::::remove(&owner, &class_instance); - let (class, instance) = class_instance; + Account::::remove(&owner, (&class, &instance)); Self::deposit_event(Event::Burned(class, instance, owner)); Ok(()) @@ -507,10 +503,8 @@ pub mod pallet { ensure!(details.owner == origin, Error::::NoPermission); ensure!(!details.is_frozen, Error::::Frozen); - let class_instance = (class, instance); - Account::::remove(&origin, &(class_instance)); - Account::::insert(&dest, &(class_instance), ()); - let (class, instance) = class_instance; + Account::::remove(&origin, (&class, &instance)); + Account::::insert(&dest, (&class, &instance), ()); details.owner = dest; Asset::::insert(&class, &instance, &details); @@ -547,10 +541,8 @@ pub mod pallet { let source = details.owner; details.owner = dest; - let class_instance = (class, instance); - Account::::remove(&source, &(class_instance)); - Account::::insert(&details.owner, &(class_instance), ()); - let (class, instance) = class_instance; + Account::::remove(&source, (&class, &instance)); + Account::::insert(&details.owner, (&class, &instance), ()); Asset::::insert(&class, &instance, &details); @@ -897,10 +889,8 @@ pub mod pallet { let source = details.owner; details.owner = dest; - let class_instance = (class, instance); - Account::::remove(&source, &(class_instance)); - Account::::insert(&details.owner, &(class_instance), ()); - let (class, instance) = class_instance; + Account::::remove(&source, (&class, &instance)); + Account::::insert(&details.owner, (&class, &instance), ()); Asset::::insert(&class, &instance, &details); From 2949d1238b40ed9016dae88ae3e9aa40b1b4c125 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 17 May 2021 08:22:54 +0200 Subject: [PATCH 11/54] use nmap --- frame/uniques/src/lib.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index b02d41b10b3f1..6c37ed956b135 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -116,12 +116,13 @@ pub mod pallet { #[pallet::storage] /// The assets held by any given account; set out this way so that assets owned by a single /// account can be enumerated. - pub(super) type Account, I: 'static = ()> = StorageDoubleMap< + pub(super) type Account, I: 'static = ()> = StorageNMap< _, - Blake2_128Concat, - T::AccountId, - Blake2_128Concat, - (T::ClassId, T::InstanceId), + ( + NMapKey, // owner + NMapKey, + NMapKey, + ), (), OptionQuery, >; @@ -379,7 +380,7 @@ pub mod pallet { ensure!(class_details.free_holds == witness.free_holds, Error::::BadWitness); for (instance, mut details) in Asset::::drain_prefix(class) { - Account::::remove(&details.owner, (class, instance)); + Account::::remove((&details.owner, class, instance)); Self::dead_instance(&mut details, &mut class_details); } debug_assert_eq!(class_details.instances, 0); @@ -428,7 +429,7 @@ pub mod pallet { Asset::::insert(&class, &instance, details); Ok(()) })?; - Account::::insert(&beneficiary, (&class, &instance), ()); + Account::::insert((&beneficiary, &class, &instance), ()); Self::deposit_event(Event::Issued(class, instance, beneficiary)); Ok(()) @@ -469,7 +470,7 @@ pub mod pallet { })?; Asset::::remove(&class, &instance); - Account::::remove(&owner, (&class, &instance)); + Account::::remove((&owner, &class, &instance)); Self::deposit_event(Event::Burned(class, instance, owner)); Ok(()) @@ -503,8 +504,8 @@ pub mod pallet { ensure!(details.owner == origin, Error::::NoPermission); ensure!(!details.is_frozen, Error::::Frozen); - Account::::remove(&origin, (&class, &instance)); - Account::::insert(&dest, (&class, &instance), ()); + Account::::remove((&origin, &class, &instance)); + Account::::insert((&dest, &class, &instance), ()); details.owner = dest; Asset::::insert(&class, &instance, &details); @@ -541,8 +542,8 @@ pub mod pallet { let source = details.owner; details.owner = dest; - Account::::remove(&source, (&class, &instance)); - Account::::insert(&details.owner, (&class, &instance), ()); + Account::::remove((&source, &class, &instance)); + Account::::insert((&details.owner, &class, &instance), ()); Asset::::insert(&class, &instance, &details); @@ -889,8 +890,8 @@ pub mod pallet { let source = details.owner; details.owner = dest; - Account::::remove(&source, (&class, &instance)); - Account::::insert(&details.owner, (&class, &instance), ()); + Account::::remove((&source, &class, &instance)); + Account::::insert((&details.owner, &class, &instance), ()); Asset::::insert(&class, &instance, &details); From 30f2f9e4baeac664b682c36aaaf6b4204095e17a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 17 May 2021 09:06:13 +0200 Subject: [PATCH 12/54] Fixes --- frame/uniques/src/lib.rs | 137 ++++++++++++++++++++----------------- frame/uniques/src/types.rs | 3 + 2 files changed, 78 insertions(+), 62 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 6c37ed956b135..d7fadd197464b 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -146,7 +146,7 @@ pub mod pallet { Blake2_128Concat, T::ClassId, ClassMetadata>, - ValueQuery, + OptionQuery, >; #[pallet::storage] @@ -158,7 +158,7 @@ pub mod pallet { Blake2_128Concat, T::InstanceId, InstanceMetadata>, - ValueQuery, + OptionQuery, >; #[pallet::event] @@ -255,11 +255,10 @@ pub mod pallet { /// /// The origin must be Signed and the sender must have sufficient funds free. /// - /// Funds of sender are reserved by `AssetDeposit`. + /// `AssetDeposit` funds of sender are reserved. /// /// Parameters: - /// - `class`: The identifier of the new asset class. This must not be currently in use to - /// identify an existing asset. + /// - `class`: The identifier of the new asset class. This must not be currently in use. /// - `admin`: The admin of this class of assets. The admin is the initial address of each /// member of the asset class's admin team. /// @@ -288,6 +287,7 @@ pub mod pallet { admin: admin.clone(), freezer: admin.clone(), deposit, + metadata_deposit: Zero::zero(), free_holding: false, instances: 0, free_holds: 0, @@ -306,11 +306,10 @@ pub mod pallet { /// /// Unlike `create`, no funds are reserved. /// - /// - `class`: The identifier of the new asset. This must not be currently in use to identify - /// an existing asset. + /// - `class`: The identifier of the new asset. This must not be currently in use. /// - `owner`: The owner of this class of assets. The owner has full superuser permissions - /// over this asset, but may later change and configure the permissions using `transfer_ownership` - /// and `set_team`. + /// over this asset, but may later change and configure the permissions using + /// `transfer_ownership` and `set_team`. /// /// Emits `ForceCreated` event when successful. /// @@ -335,6 +334,7 @@ pub mod pallet { admin: owner.clone(), freezer: owner.clone(), deposit: Zero::zero(), + metadata_deposit: Zero::zero(), free_holding, instances: 0, free_holds: 0, @@ -348,16 +348,17 @@ pub mod pallet { /// Destroy a class of fungible assets. /// /// The origin must conform to `ForceOrigin` or must be `Signed` and the sender must be the - /// owner of the asset `id`. + /// owner of the asset `class`. /// - /// - `class`: The identifier of the asset to be destroyed. This must identify an existing - /// asset. + /// - `class`: The identifier of the asset class to be destroyed. + /// - `witness`: Information on the instances minted in the asset class. This must be + /// correct. /// /// Emits `Destroyed` event when successful. /// - /// Weight: `O(c + p + a)` where: - /// - `c = (witness.accounts - witness.sufficients)` - /// - `s = witness.sufficients` + /// Weight: `O(i + f)` where: + /// - `i = (witness.instances - witness.free_holds)` + /// - `f = witness.free_holds` #[pallet::weight(T::WeightInfo::destroy( witness.instances.saturating_sub(witness.free_holds), witness.free_holds, @@ -379,17 +380,18 @@ pub mod pallet { ensure!(class_details.instances == witness.instances, Error::::BadWitness); ensure!(class_details.free_holds == witness.free_holds, Error::::BadWitness); - for (instance, mut details) in Asset::::drain_prefix(class) { - Account::::remove((&details.owner, class, instance)); + for (instance, mut details) in Asset::::drain_prefix(&class) { + Account::::remove((&details.owner, &class, &instance)); + InstanceMetadataOf::::remove(&class, &instance); Self::dead_instance(&mut details, &mut class_details); } debug_assert_eq!(class_details.instances, 0); debug_assert_eq!(class_details.free_holds, 0); - let metadata = ClassMetadataOf::::take(&class); + ClassMetadataOf::::remove(&class); T::Currency::unreserve( &class_details.owner, - class_details.deposit.saturating_add(metadata.deposit), + class_details.deposit.saturating_add(class_details.metadata_deposit), ); Self::deposit_event(Event::Destroyed(class)); @@ -401,11 +403,11 @@ pub mod pallet { /// Mint an asset instance of a particular class. /// - /// The origin must be Signed and the sender must be the Issuer of the asset `id`. + /// The origin must be Signed and the sender must be the Issuer of the asset `class`. /// /// - `class`: The class of the asset to be minted. /// - `instance`: The instance value of the asset to be minted. - /// - `beneficiary`: The account to be credited with the minted asset. + /// - `beneficiary`: The initial owner of the minted asset. /// /// Emits `Issued` event when successful. /// @@ -437,7 +439,7 @@ pub mod pallet { /// Destroy a single asset instance. /// - /// Origin must be Signed and the sender should be the Manager of the asset `id`. + /// Origin must be Signed and the sender should be the Admin of the asset `class`. /// /// - `class`: The class of the asset to be burned. /// - `instance`: The instance of the asset to be burned. @@ -466,9 +468,16 @@ pub mod pallet { ensure!(is_permitted, Error::::NoPermission); ensure!(check_owner.map_or(true, |o| o == details.owner), Error::::WrongOwner); Self::dead_instance(&mut details, class_details); + if let Some(metadata) = InstanceMetadataOf::::take(&class, &instance) { + // Remove instance metadata + class_details.metadata_deposit = class_details.metadata_deposit + .saturating_sub(metadata.deposit); + T::Currency::unreserve(&class_details.owner, metadata.deposit); + } Ok(details.owner) })?; + Asset::::remove(&class, &instance); Account::::remove((&owner, &class, &instance)); @@ -478,7 +487,7 @@ pub mod pallet { /// Move an asset from the sender account to another. /// - /// Origin must be Signed and the signing account must be the owner of the asset instance.. + /// Origin must be Signed and the signing account must be the owner of the asset `instance`. /// /// - `class`: The class of the asset to be transferred. /// - `instance`: The instance of the asset to be transferred. @@ -516,7 +525,7 @@ pub mod pallet { /// Move some assets from one account to another. /// - /// Origin must be Signed and the sender should be the Admin of the asset `id`. + /// Origin must be Signed and the sender should be the Admin of the asset `class`. /// /// - `class`: The class of the asset to be transferred. /// - `instance`: The instance of the asset to be transferred. @@ -582,7 +591,7 @@ pub mod pallet { Ok(()) } - /// Allow unprivileged transfer of an asset instance. + /// Re-allow unprivileged transfer of an asset instance. /// /// Origin must be Signed and the sender should be the Freezer of the asset `class`. /// @@ -612,7 +621,7 @@ pub mod pallet { Ok(()) } - /// Disallow further unprivileged transfers for the asset class. + /// Disallow further unprivileged transfers for a whole asset class. /// /// Origin must be Signed and the sender should be the Freezer of the asset `class`. /// @@ -639,7 +648,7 @@ pub mod pallet { }) } - /// Allow unprivileged transfers for the asset again. + /// Re-allow unprivileged transfers for a whole asset class. /// /// Origin must be Signed and the sender should be the Admin of the asset `class`. /// @@ -670,8 +679,8 @@ pub mod pallet { /// /// Origin must be Signed and the sender should be the Owner of the asset `class`. /// - /// - `class`: The identifier of the asset. - /// - `owner`: The new Owner of this asset. + /// - `class`: The asset class whose owner should be changed. + /// - `owner`: The new Owner of this asset class. /// /// Emits `OwnerChanged`. /// @@ -692,8 +701,7 @@ pub mod pallet { return Ok(()); } - let metadata_deposit = ClassMetadataOf::::get(class).deposit; - let deposit = details.deposit + metadata_deposit; + let deposit = details.deposit + details.metadata_deposit; // Move the deposit to the new owner. T::Currency::repatriate_reserved(&details.owner, &owner, deposit, Reserved)?; @@ -709,10 +717,10 @@ pub mod pallet { /// /// Origin must be Signed and the sender should be the Owner of the asset `class`. /// - /// - `class`: The identifier of the asset to be frozen. - /// - `issuer`: The new Issuer of this asset. - /// - `admin`: The new Admin of this asset. - /// - `freezer`: The new Freezer of this asset. + /// - `class`: The asset class whose team should be changed. + /// - `issuer`: The new Issuer of this asset class. + /// - `admin`: The new Admin of this asset class. + /// - `freezer`: The new Freezer of this asset class. /// /// Emits `TeamChanged`. /// @@ -747,9 +755,9 @@ pub mod pallet { /// /// Origin must be Signed and must be the owner of the asset `instance`. /// - /// - `class`: The class of the asset to be approved for transfer. - /// - `instance`: The instance of the asset to be approved for transfer. - /// - `delegate`: The account to delegate permission to transfer asset. + /// - `class`: The class of the asset to be approved for delegated transfer. + /// - `instance`: The instance of the asset to be approved for delegated transfer. + /// - `delegate`: The account to delegate permission to transfer the asset. /// /// Emits `ApprovedTransfer` on success. /// @@ -861,8 +869,8 @@ pub mod pallet { /// Transfer some asset instance from a target account to a destination account by an /// approved delegate. /// - /// Origin must be Signed and there must be an approval in place by the `owner` to the - /// signer. + /// Origin must be Signed and there must be an approval in place for the signer on this + /// asset `instance`. /// /// - `class`: The class of the asset to transfer. /// - `instance`: The instance of the asset to transfer. @@ -909,13 +917,8 @@ pub mod pallet { /// - `issuer`: The new Issuer of this asset. /// - `admin`: The new Admin of this asset. /// - `freezer`: The new Freezer of this asset. - /// - `min_balance`: The minimum balance of this new asset that any single account must - /// have. If an account's balance is reduced below this, then it collapses to zero. - /// - `is_sufficient`: Whether a non-zero balance of this asset is deposit of sufficient - /// value to account for the state bloat associated with its balance storage. If set to - /// `true`, then non-zero balances may be stored without a `consumer` reference (and thus - /// an ED in the Balances pallet or whatever else is used to control user-account state - /// growth). + /// - `free_holding`: Whether a deposit is taken for holding an instance of this asset + /// class. /// - `is_frozen`: Whether this asset class is frozen except for permissioned/admin /// instructions. /// @@ -953,16 +956,16 @@ pub mod pallet { /// Set the metadata for an asset instance. /// /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the - /// asset `instance`. + /// asset `class`. /// - /// Funds of sender are reserved according to the formula: - /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into + /// If the origin is Signed, then funds of signer are reserved according to the formula: + /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + info.len)` taking into /// account any already reserved funds. /// - /// - `class`: The identifier of the asset class whose instance's metadata to clear. - /// - `instance`: The identifier of the asset instance whose metadata to clear. + /// - `class`: The identifier of the asset class whose instance's metadata to set. + /// - `instance`: The identifier of the asset instance whose metadata to set. /// - `name`: The user visible name of this asset. Limited in length by `StringLimit`. - /// - `name`: The general information of this asset. Limited in length by `StringLimit`. + /// - `info`: The general information of this asset. Limited in length by `StringLimit`. /// - `is_frozen`: Whether the metadata should be frozen against further changes. /// /// Emits `MetadataSet`. @@ -984,10 +987,11 @@ pub mod pallet { ensure!(name.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); ensure!(info.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + let mut class_details = Class::::get(&class) + .ok_or(Error::::Unknown)?; + if let Some(check_owner) = &maybe_check_owner { - let details = Asset::::get(&class, &instance) - .ok_or(Error::::Unknown)?; - ensure!(check_owner == &details.owner, Error::::NoPermission); + ensure!(check_owner == &class_details.owner, Error::::NoPermission); } InstanceMetadataOf::::try_mutate_exists(class, instance, |metadata| { @@ -995,6 +999,8 @@ pub mod pallet { ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); + class_details.metadata_deposit = class_details.metadata_deposit + .saturating_sub(old_deposit); let deposit = if let Some(owner) = maybe_check_owner { let deposit = T::MetadataDepositPerByte::get() .saturating_mul((name.len() as u32).into()) @@ -1005,10 +1011,13 @@ pub mod pallet { } else { T::Currency::unreserve(&owner, old_deposit - deposit); } + deposit } else { old_deposit }; + class_details.metadata_deposit =class_details.metadata_deposit + .saturating_add(deposit); *metadata = Some(InstanceMetadata { deposit, @@ -1017,6 +1026,7 @@ pub mod pallet { is_frozen, }); + Class::::insert(&class, &class_details); Self::deposit_event(Event::MetadataSet(class, name, info, false)); Ok(()) }) @@ -1045,10 +1055,10 @@ pub mod pallet { .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - let details = Asset::::get(&class, &instance) + let mut class_details = Class::::get(&class) .ok_or(Error::::Unknown)?; if let Some(check_owner) = &maybe_check_owner { - ensure!(check_owner == &details.owner, Error::::NoPermission); + ensure!(check_owner == &class_details.owner, Error::::NoPermission); } InstanceMetadataOf::::try_mutate_exists(class, instance, |metadata| { @@ -1056,7 +1066,10 @@ pub mod pallet { ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; - T::Currency::unreserve(&details.owner, deposit); + T::Currency::unreserve(&class_details.owner, deposit); + class_details.metadata_deposit = class_details.metadata_deposit.saturating_sub(deposit); + + Class::::insert(&class, &class_details); Self::deposit_event(Event::MetadataCleared(class)); Ok(()) }) @@ -1067,11 +1080,11 @@ pub mod pallet { /// Origin must be either `ForceOrigin` or `Signed` and the sender should be the Owner of /// the asset `class`. /// - /// Funds of sender are reserved according to the formula: - /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into + /// If the origin is `Signed`, then funds of signer are reserved according to the formula: + /// `MetadataDepositBase + MetadataDepositPerByte * name.len` taking into /// account any already reserved funds. /// - /// - `class`: The identifier of the asset to update. + /// - `class`: The identifier of the asset whose metadata to update. /// - `name`: The user visible name of this asset. Limited in length by `StringLimit`. /// - `is_frozen`: Whether the metadata should be frozen against further changes. /// diff --git a/frame/uniques/src/types.rs b/frame/uniques/src/types.rs index 47301df5ed17c..45451cddb95ed 100644 --- a/frame/uniques/src/types.rs +++ b/frame/uniques/src/types.rs @@ -37,6 +37,9 @@ pub struct ClassDetails< pub(super) freezer: AccountId, /// The balance deposited for this asset. This pays for the data stored here. pub(super) deposit: DepositBalance, + /// The total balance deposited for the all metadata associated with this asset (both class and + /// instances). + pub(super) metadata_deposit: DepositBalance, /// If `true`, then no deposit is needed to hold instances of this class. pub(super) free_holding: bool, /// The total number of outstanding unique instances of this asset class. From 00c8e04bb734ddeb19f12ae6d66d37b6c537bf2c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 17 May 2021 09:22:55 +0200 Subject: [PATCH 13/54] class metadata has information field --- frame/uniques/src/lib.rs | 10 ++++++---- frame/uniques/src/types.rs | 4 ++++ frame/uniques/src/weights.rs | 8 +++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index d7fadd197464b..d2b5e54ee6e4e 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -208,8 +208,8 @@ pub mod pallet { /// An asset `class` has had its attributes changed by the `Force` origin. /// \[ class \] AssetStatusChanged(T::ClassId), - /// New metadata has been set for an asset class. \[ asset_id, name, is_frozen \] - ClassMetadataSet(T::ClassId, Vec, bool), + /// New metadata has been set for an asset class. \[ asset_id, name, info, is_frozen \] + ClassMetadataSet(T::ClassId, Vec, Vec, bool), /// Metadata has been cleared for an asset class. \[ asset_id \] ClassMetadataCleared(T::ClassId), /// New metadata has been set for an asset instance. \[ asset_id, name, info, is_frozen \] @@ -1091,11 +1091,12 @@ pub mod pallet { /// Emits `ClassMetadataSet`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::set_class_metadata(name.len() as u32))] + #[pallet::weight(T::WeightInfo::set_class_metadata(name.len() as u32, info.len() as u32))] pub(super) fn set_class_metadata( origin: OriginFor, #[pallet::compact] class: T::ClassId, name: Vec, + info: Vec, is_frozen: bool, ) -> DispatchResult { let maybe_check_owner = T::ForceOrigin::try_origin(origin) @@ -1132,10 +1133,11 @@ pub mod pallet { *metadata = Some(ClassMetadata { deposit, name: name.clone(), + information: info.clone(), is_frozen, }); - Self::deposit_event(Event::ClassMetadataSet(class, name, false)); + Self::deposit_event(Event::ClassMetadataSet(class, name, info, false)); Ok(()) }) } diff --git a/frame/uniques/src/types.rs b/frame/uniques/src/types.rs index 45451cddb95ed..519521fa0d8e7 100644 --- a/frame/uniques/src/types.rs +++ b/frame/uniques/src/types.rs @@ -92,6 +92,10 @@ pub struct ClassMetadata { pub(super) deposit: DepositBalance, /// The user friendly name of this asset. Limited in length by `StringLimit`. pub(super) name: Vec, + /// General information concerning this asset. Limited in length by `StringLimit`. This will + /// generally be either a JSON dump or the hash of some JSON which can be found on a + /// hash-addressable global publication system such as IPFS. + pub(super) information: Vec, /// Whether the asset metadata may be changed by a non Force origin. pub(super) is_frozen: bool, } diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index bb4fa1a496b2a..b62bacd1e217c 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -64,7 +64,7 @@ pub trait WeightInfo { fn force_asset_status() -> Weight; fn set_metadata(n: u32, i: u32, ) -> Weight; fn clear_metadata() -> Weight; - fn set_class_metadata(n: u32, ) -> Weight; + fn set_class_metadata(n: u32, i: u32, ) -> Weight; fn clear_class_metadata() -> Weight; } @@ -182,10 +182,11 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn set_class_metadata(n: u32, ) -> Weight { + fn set_class_metadata(n: u32, i: u32, ) -> Weight { (53_367_000 as Weight) // Standard Error: 0 .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((8_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -309,10 +310,11 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn set_class_metadata(n: u32, ) -> Weight { + fn set_class_metadata(n: u32, i: u32, ) -> Weight { (53_367_000 as Weight) // Standard Error: 0 .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) + .saturating_add((8_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } From 326bebadcc844bc67bbb86232fadfe0910e7a8c4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 19 May 2021 11:16:21 +0200 Subject: [PATCH 14/54] Intiial mock/tests and a fix --- frame/support/src/storage/types/nmap.rs | 2 +- frame/uniques/src/functions.rs | 4 +- frame/uniques/src/lib.rs | 16 +- frame/uniques/src/mock.rs | 51 +- frame/uniques/src/tests.rs | 642 ++++++++++++------------ 5 files changed, 349 insertions(+), 366 deletions(-) diff --git a/frame/support/src/storage/types/nmap.rs b/frame/support/src/storage/types/nmap.rs index 1a2b6d4d55dcc..6b843e7d23774 100755 --- a/frame/support/src/storage/types/nmap.rs +++ b/frame/support/src/storage/types/nmap.rs @@ -263,7 +263,7 @@ where /// Iter over all value of the storage. /// - /// NOTE: If a value failed to decode becaues storage is corrupted then it is skipped. + /// NOTE: If a value failed to decode because storage is corrupted then it is skipped. pub fn iter_values() -> crate::storage::PrefixIterator { >::iter_values() } diff --git a/frame/uniques/src/functions.rs b/frame/uniques/src/functions.rs index 53162aff29794..d9dad4b9e900a 100644 --- a/frame/uniques/src/functions.rs +++ b/frame/uniques/src/functions.rs @@ -77,7 +77,7 @@ impl, I: 'static> Pallet { class_details.free_holds += 1; } else { T::Currency::transfer( - &owner, + &class_details.owner, &Self::into_account(), deposit, AllowDeath, @@ -100,7 +100,7 @@ impl, I: 'static> Pallet { // Return the deposit. let ok = T::Currency::transfer( &Self::into_account(), - &instance_details.owner, + &class_details.owner, instance_details.deposit, AllowDeath, ).is_ok(); diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index d2b5e54ee6e4e..81e53576aae02 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -32,12 +32,11 @@ pub mod weights; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; */ -/* TODO: #[cfg(test)] pub mod mock; #[cfg(test)] mod tests; -*/ + // TODO: mod impl_non_fungibles; mod functions; mod types; @@ -94,9 +93,6 @@ pub mod pallet { /// metadata. type MetadataDepositPerByte: Get>; - /// The amount of funds that must be reserved when creating a new approval. - type ApprovalDeposit: Get>; - /// The maximum length of a name or symbol stored on-chain. type StringLimit: Get; @@ -1003,7 +999,7 @@ pub mod pallet { .saturating_sub(old_deposit); let deposit = if let Some(owner) = maybe_check_owner { let deposit = T::MetadataDepositPerByte::get() - .saturating_mul((name.len() as u32).into()) + .saturating_mul(((name.len() + info.len()) as u32).into()) .saturating_add(T::MetadataDepositBase::get()); if deposit > old_deposit { @@ -1105,8 +1101,8 @@ pub mod pallet { ensure!(name.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + let mut details = Class::::get(&class).ok_or(Error::::Unknown)?; if let Some(check_owner) = &maybe_check_owner { - let details = Class::::get(&class).ok_or(Error::::Unknown)?; ensure!(check_owner == &details.owner, Error::::NoPermission); } @@ -1115,9 +1111,10 @@ pub mod pallet { ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); + details.metadata_deposit = details.metadata_deposit.saturating_sub(old_deposit); let deposit = if let Some(owner) = maybe_check_owner { let deposit = T::MetadataDepositPerByte::get() - .saturating_mul((name.len() as u32).into()) + .saturating_mul(((name.len() + info.len()) as u32).into()) .saturating_add(T::MetadataDepositBase::get()); if deposit > old_deposit { @@ -1129,6 +1126,9 @@ pub mod pallet { } else { old_deposit }; + details.metadata_deposit = details.metadata_deposit.saturating_add(deposit); + + Class::::insert(&class, details); *metadata = Some(ClassMetadata { deposit, diff --git a/frame/uniques/src/mock.rs b/frame/uniques/src/mock.rs index 0b7aa339835ec..e18053b1371c7 100644 --- a/frame/uniques/src/mock.rs +++ b/frame/uniques/src/mock.rs @@ -18,7 +18,7 @@ //! Test environment for Assets pallet. use super::*; -use crate as pallet_assets; +use crate as pallet_uniques; use sp_core::H256; use sp_runtime::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; @@ -35,7 +35,7 @@ construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Assets: pallet_assets::{Pallet, Call, Storage, Event}, + Uniques: pallet_uniques::{Pallet, Call, Storage, Event}, } ); @@ -83,8 +83,8 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub const AssetDeposit: u64 = 1; - pub const ApprovalDeposit: u64 = 1; + pub const ClassDeposit: u64 = 2; + pub const InstanceDeposit: u64 = 1; pub const StringLimit: u32 = 50; pub const MetadataDepositBase: u64 = 1; pub const MetadataDepositPerByte: u64 = 1; @@ -92,51 +92,16 @@ parameter_types! { impl Config for Test { type Event = Event; - type Balance = u64; - type AssetId = u32; + type ClassId = u32; + type InstanceId = u32; type Currency = Balances; type ForceOrigin = frame_system::EnsureRoot; - type AssetDeposit = AssetDeposit; + type ClassDeposit = ClassDeposit; + type InstanceDeposit = InstanceDeposit; type MetadataDepositBase = MetadataDepositBase; type MetadataDepositPerByte = MetadataDepositPerByte; - type ApprovalDeposit = ApprovalDeposit; type StringLimit = StringLimit; - type Freezer = TestFreezer; type WeightInfo = (); - type Extra = (); -} - -use std::cell::RefCell; -use std::collections::HashMap; - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub(crate) enum Hook { - Died(u32, u64), -} -thread_local! { - static FROZEN: RefCell> = RefCell::new(Default::default()); - static HOOKS: RefCell> = RefCell::new(Default::default()); -} - -pub struct TestFreezer; -impl FrozenBalance for TestFreezer { - fn frozen_balance(asset: u32, who: &u64) -> Option { - FROZEN.with(|f| f.borrow().get(&(asset, who.clone())).cloned()) - } - - fn died(asset: u32, who: &u64) { - HOOKS.with(|h| h.borrow_mut().push(Hook::Died(asset, who.clone()))); - } -} - -pub(crate) fn set_frozen_balance(asset: u32, who: u64, amount: u64) { - FROZEN.with(|f| f.borrow_mut().insert((asset, who), amount)); -} -pub(crate) fn clear_frozen_balance(asset: u32, who: u64) { - FROZEN.with(|f| f.borrow_mut().remove(&(asset, who))); -} -pub(crate) fn hooks() -> Vec { - HOOKS.with(|h| h.borrow().clone()) } pub(crate) fn new_test_ext() -> sp_io::TestExternalities { diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index f4976af023627..571598c7a7fe1 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -15,119 +15,51 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Tests for Assets pallet. +//! Tests for Uniques pallet. use super::*; -use crate::{Error, mock::*}; -use sp_runtime::TokenError; +use crate::mock::*; use frame_support::{assert_ok, assert_noop, traits::Currency}; -use pallet_balances::Error as BalancesError; - +/* fn last_event() -> mock::Event { frame_system::Pallet::::events().pop().expect("Event expected").event } - -#[test] -fn basic_minting_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::mint(Origin::signed(1), 0, 2, 100)); - assert_eq!(Assets::balance(0, 2), 100); - }); +*/ +fn assets() -> Vec<(u64, u32, u32)> { + let mut r: Vec<_> = Account::::iter().map(|x| x.0).collect(); + r.sort(); + r } - -#[test] -fn approval_lifecycle_works() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); - assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Balances::reserved_balance(&1), 1); - assert_ok!(Assets::transfer_approved(Origin::signed(2), 0, 1, 3, 40)); - assert_ok!(Assets::cancel_approval(Origin::signed(1), 0, 2)); - assert_eq!(Assets::balance(0, 1), 60); - assert_eq!(Assets::balance(0, 3), 40); - assert_eq!(Balances::reserved_balance(&1), 0); - }); +/* +fn assets_of(who: u64) -> Vec<(u32, u32)> { + let mut r: Vec<_> = Account::::iter_prefix((who,)).map(|x| x.0).collect(); + r.sort(); + r } -#[test] -fn approval_deposits_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - let e = BalancesError::::InsufficientBalance; - assert_noop!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50), e); - - Balances::make_free_balance_be(&1, 1); - assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Balances::reserved_balance(&1), 1); - - assert_ok!(Assets::transfer_approved(Origin::signed(2), 0, 1, 3, 50)); - assert_eq!(Balances::reserved_balance(&1), 0); - - assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); - assert_ok!(Assets::cancel_approval(Origin::signed(1), 0, 2)); - assert_eq!(Balances::reserved_balance(&1), 0); - }); +fn assets_of_class(who: u64, class: u32) -> Vec { + let mut r: Vec<_> = Account::::iter_prefix((who, class)).map(|x| x.0).collect(); + r.sort(); + r } - +*/ #[test] -fn cannot_transfer_more_than_approved() { +fn basic_setup_works() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); - assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); - let e = Error::::Unapproved; - assert_noop!(Assets::transfer_approved(Origin::signed(2), 0, 1, 3, 51), e); + assert_eq!(assets(), vec![]); }); } #[test] -fn cannot_transfer_more_than_exists() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); - assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 101)); - let e = Error::::BalanceLow; - assert_noop!(Assets::transfer_approved(Origin::signed(2), 0, 1, 3, 101), e); - }); -} - -#[test] -fn cancel_approval_works() { +fn basic_minting_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); - assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); - assert_noop!(Assets::cancel_approval(Origin::signed(1), 1, 2), Error::::Unknown); - assert_noop!(Assets::cancel_approval(Origin::signed(2), 0, 2), Error::::Unknown); - assert_noop!(Assets::cancel_approval(Origin::signed(1), 0, 3), Error::::Unknown); - assert_ok!(Assets::cancel_approval(Origin::signed(1), 0, 2)); - assert_noop!(Assets::cancel_approval(Origin::signed(1), 0, 2), Error::::Unknown); - }); -} + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); + assert_eq!(assets(), vec![(1, 0, 42)]); -#[test] -fn force_cancel_approval_works() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 1); - assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); - let e = Error::::NoPermission; - assert_noop!(Assets::force_cancel_approval(Origin::signed(2), 0, 1, 2), e); - assert_noop!(Assets::force_cancel_approval(Origin::signed(1), 1, 1, 2), Error::::Unknown); - assert_noop!(Assets::force_cancel_approval(Origin::signed(1), 0, 2, 2), Error::::Unknown); - assert_noop!(Assets::force_cancel_approval(Origin::signed(1), 0, 1, 3), Error::::Unknown); - assert_ok!(Assets::force_cancel_approval(Origin::signed(1), 0, 1, 2)); - assert_noop!(Assets::force_cancel_approval(Origin::signed(1), 0, 1, 2), Error::::Unknown); + assert_ok!(Uniques::force_create(Origin::root(), 1, 2, true)); + assert_ok!(Uniques::mint(Origin::signed(2), 1, 69, 1)); + assert_eq!(assets(), vec![(1, 0, 42), (1, 1, 69)]); }); } @@ -135,47 +67,38 @@ fn force_cancel_approval_works() { fn lifecycle_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::create(Origin::signed(1), 0, 1, 1)); - assert_eq!(Balances::reserved_balance(&1), 1); - assert!(Asset::::contains_key(0)); - - assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0], vec![0], 12)); - assert_eq!(Balances::reserved_balance(&1), 4); - assert!(Metadata::::contains_key(0)); - - Balances::make_free_balance_be(&10, 100); - assert_ok!(Assets::mint(Origin::signed(1), 0, 10, 100)); - Balances::make_free_balance_be(&20, 100); - assert_ok!(Assets::mint(Origin::signed(1), 0, 20, 100)); - assert_eq!(Account::::iter_prefix(0).count(), 2); - - let w = Asset::::get(0).unwrap().destroy_witness(); - assert_ok!(Assets::destroy(Origin::signed(1), 0, w)); - assert_eq!(Balances::reserved_balance(&1), 0); - - assert!(!Asset::::contains_key(0)); - assert!(!Metadata::::contains_key(0)); - assert_eq!(Account::::iter_prefix(0).count(), 0); - - assert_ok!(Assets::create(Origin::signed(1), 0, 1, 1)); - assert_eq!(Balances::reserved_balance(&1), 1); - assert!(Asset::::contains_key(0)); - - assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0], vec![0], 12)); - assert_eq!(Balances::reserved_balance(&1), 4); - assert!(Metadata::::contains_key(0)); - - assert_ok!(Assets::mint(Origin::signed(1), 0, 10, 100)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 20, 100)); - assert_eq!(Account::::iter_prefix(0).count(), 2); - - let w = Asset::::get(0).unwrap().destroy_witness(); - assert_ok!(Assets::destroy(Origin::root(), 0, w)); + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_eq!(Balances::reserved_balance(&1), 2); + + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0], vec![0], false)); + assert_eq!(Balances::reserved_balance(&1), 5); + assert!(ClassMetadataOf::::contains_key(0)); + + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 10)); + assert_eq!(Balances::total_balance(&1), 99); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 69, 20)); + assert_eq!(Balances::total_balance(&1), 98); + assert_eq!(assets(), vec![(10, 0, 42), (20, 0, 69)]); + + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![42], vec![42], false)); + assert_eq!(Balances::reserved_balance(&1), 8); + assert!(InstanceMetadataOf::::contains_key(0, 42)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, vec![69], vec![69], false)); + assert_eq!(Balances::reserved_balance(&1), 11); + assert!(InstanceMetadataOf::::contains_key(0, 69)); + + let w = Class::::get(0).unwrap().destroy_witness(); + assert_ok!(Uniques::destroy(Origin::signed(1), 0, w)); + assert_eq!(Balances::total_balance(&1), 100); assert_eq!(Balances::reserved_balance(&1), 0); - assert!(!Asset::::contains_key(0)); - assert!(!Metadata::::contains_key(0)); - assert_eq!(Account::::iter_prefix(0).count(), 0); + assert!(!Class::::contains_key(0)); + assert!(!Asset::::contains_key(0, 42)); + assert!(!Asset::::contains_key(0, 69)); + assert!(!ClassMetadataOf::::contains_key(0)); + assert!(!InstanceMetadataOf::::contains_key(0, 42)); + assert!(!InstanceMetadataOf::::contains_key(0, 69)); + assert_eq!(assets(), vec![]); }); } @@ -183,60 +106,61 @@ fn lifecycle_should_work() { fn destroy_with_bad_witness_should_not_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - let w = Asset::::get(0).unwrap().destroy_witness(); - assert_ok!(Assets::mint(Origin::signed(1), 0, 10, 100)); - assert_noop!(Assets::destroy(Origin::signed(1), 0, w), Error::::BadWitness); + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + + let w = Class::::get(0).unwrap().destroy_witness(); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); + assert_noop!(Uniques::destroy(Origin::signed(1), 0, w), Error::::BadWitness); }); } - +/* #[test] fn non_providing_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, false, 1)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false, 1)); Balances::make_free_balance_be(&0, 100); - assert_ok!(Assets::mint(Origin::signed(1), 0, 0, 100)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 100)); // Cannot mint into account 2 since it doesn't (yet) exist... - assert_noop!(Assets::mint(Origin::signed(1), 0, 1, 100), TokenError::CannotCreate); + assert_noop!(Uniques::mint(Origin::signed(1), 0, 1, 100), TokenError::CannotCreate); // ...or transfer... - assert_noop!(Assets::transfer(Origin::signed(0), 0, 1, 50), TokenError::CannotCreate); + assert_noop!(Uniques::transfer(Origin::signed(0), 0, 1, 50), TokenError::CannotCreate); // ...or force-transfer - assert_noop!(Assets::force_transfer(Origin::signed(1), 0, 0, 1, 50), TokenError::CannotCreate); + assert_noop!(Uniques::force_transfer(Origin::signed(1), 0, 0, 1, 50), TokenError::CannotCreate); Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 100); - assert_ok!(Assets::transfer(Origin::signed(0), 0, 1, 25)); - assert_ok!(Assets::force_transfer(Origin::signed(1), 0, 0, 2, 25)); + assert_ok!(Uniques::transfer(Origin::signed(0), 0, 1, 25)); + assert_ok!(Uniques::force_transfer(Origin::signed(1), 0, 0, 2, 25)); }); } #[test] fn min_balance_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 10)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 10)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Asset::::get(0).unwrap().accounts, 1); // Cannot create a new account with a balance that is below minimum... - assert_noop!(Assets::mint(Origin::signed(1), 0, 2, 9), TokenError::BelowMinimum); - assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 9), TokenError::BelowMinimum); - assert_noop!(Assets::force_transfer(Origin::signed(1), 0, 1, 2, 9), TokenError::BelowMinimum); + assert_noop!(Uniques::mint(Origin::signed(1), 0, 2, 9), TokenError::BelowMinimum); + assert_noop!(Uniques::transfer(Origin::signed(1), 0, 2, 9), TokenError::BelowMinimum); + assert_noop!(Uniques::force_transfer(Origin::signed(1), 0, 1, 2, 9), TokenError::BelowMinimum); // When deducting from an account to below minimum, it should be reaped. - assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 91)); - assert!(Assets::balance(0, 1).is_zero()); - assert_eq!(Assets::balance(0, 2), 100); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 91)); + assert!(Uniques::balance(0, 1).is_zero()); + assert_eq!(Uniques::balance(0, 2), 100); assert_eq!(Asset::::get(0).unwrap().accounts, 1); - assert_ok!(Assets::force_transfer(Origin::signed(1), 0, 2, 1, 91)); - assert!(Assets::balance(0, 2).is_zero()); - assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Uniques::force_transfer(Origin::signed(1), 0, 2, 1, 91)); + assert!(Uniques::balance(0, 2).is_zero()); + assert_eq!(Uniques::balance(0, 1), 100); assert_eq!(Asset::::get(0).unwrap().accounts, 1); - assert_ok!(Assets::burn(Origin::signed(1), 0, 1, 91)); - assert!(Assets::balance(0, 1).is_zero()); + assert_ok!(Uniques::burn(Origin::signed(1), 0, 1, 91)); + assert!(Uniques::balance(0, 1).is_zero()); assert_eq!(Asset::::get(0).unwrap().accounts, 0); }); } @@ -244,86 +168,86 @@ fn min_balance_should_work() { #[test] fn querying_total_supply_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 50); - assert_ok!(Assets::transfer(Origin::signed(2), 0, 3, 31)); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 19); - assert_eq!(Assets::balance(0, 3), 31); - assert_ok!(Assets::burn(Origin::signed(1), 0, 3, u64::max_value())); - assert_eq!(Assets::total_supply(0), 69); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Uniques::balance(0, 1), 100); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Uniques::balance(0, 1), 50); + assert_eq!(Uniques::balance(0, 2), 50); + assert_ok!(Uniques::transfer(Origin::signed(2), 0, 3, 31)); + assert_eq!(Uniques::balance(0, 1), 50); + assert_eq!(Uniques::balance(0, 2), 19); + assert_eq!(Uniques::balance(0, 3), 31); + assert_ok!(Uniques::burn(Origin::signed(1), 0, 3, u64::max_value())); + assert_eq!(Uniques::total_supply(0), 69); }); } #[test] fn transferring_amount_below_available_balance_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 50); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Uniques::balance(0, 1), 100); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Uniques::balance(0, 1), 50); + assert_eq!(Uniques::balance(0, 2), 50); }); } #[test] fn transferring_enough_to_kill_source_when_keep_alive_should_fail() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 10)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_noop!(Assets::transfer_keep_alive(Origin::signed(1), 0, 2, 91), Error::::BalanceLow); - assert_ok!(Assets::transfer_keep_alive(Origin::signed(1), 0, 2, 90)); - assert_eq!(Assets::balance(0, 1), 10); - assert_eq!(Assets::balance(0, 2), 90); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 10)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Uniques::balance(0, 1), 100); + assert_noop!(Uniques::transfer_keep_alive(Origin::signed(1), 0, 2, 91), Error::::BalanceLow); + assert_ok!(Uniques::transfer_keep_alive(Origin::signed(1), 0, 2, 90)); + assert_eq!(Uniques::balance(0, 1), 10); + assert_eq!(Uniques::balance(0, 2), 90); }); } #[test] fn transferring_frozen_user_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::freeze(Origin::signed(1), 0, 1)); - assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 50), Error::::Frozen); - assert_ok!(Assets::thaw(Origin::signed(1), 0, 1)); - assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Uniques::balance(0, 1), 100); + assert_ok!(Uniques::freeze(Origin::signed(1), 0, 1)); + assert_noop!(Uniques::transfer(Origin::signed(1), 0, 2, 50), Error::::Frozen); + assert_ok!(Uniques::thaw(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); }); } #[test] fn transferring_frozen_asset_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::freeze_asset(Origin::signed(1), 0)); - assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 50), Error::::Frozen); - assert_ok!(Assets::thaw_asset(Origin::signed(1), 0)); - assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Uniques::balance(0, 1), 100); + assert_ok!(Uniques::freeze_asset(Origin::signed(1), 0)); + assert_noop!(Uniques::transfer(Origin::signed(1), 0, 2, 50), Error::::Frozen); + assert_ok!(Uniques::thaw_asset(Origin::signed(1), 0)); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); }); } #[test] fn origin_guards_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_noop!(Assets::transfer_ownership(Origin::signed(2), 0, 2), Error::::NoPermission); - assert_noop!(Assets::set_team(Origin::signed(2), 0, 2, 2, 2), Error::::NoPermission); - assert_noop!(Assets::freeze(Origin::signed(2), 0, 1), Error::::NoPermission); - assert_noop!(Assets::thaw(Origin::signed(2), 0, 2), Error::::NoPermission); - assert_noop!(Assets::mint(Origin::signed(2), 0, 2, 100), Error::::NoPermission); - assert_noop!(Assets::burn(Origin::signed(2), 0, 1, 100), Error::::NoPermission); - assert_noop!(Assets::force_transfer(Origin::signed(2), 0, 1, 2, 100), Error::::NoPermission); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_noop!(Uniques::transfer_ownership(Origin::signed(2), 0, 2), Error::::NoPermission); + assert_noop!(Uniques::set_team(Origin::signed(2), 0, 2, 2, 2), Error::::NoPermission); + assert_noop!(Uniques::freeze(Origin::signed(2), 0, 1), Error::::NoPermission); + assert_noop!(Uniques::thaw(Origin::signed(2), 0, 2), Error::::NoPermission); + assert_noop!(Uniques::mint(Origin::signed(2), 0, 2, 100), Error::::NoPermission); + assert_noop!(Uniques::burn(Origin::signed(2), 0, 1, 100), Error::::NoPermission); + assert_noop!(Uniques::force_transfer(Origin::signed(2), 0, 1, 2, 100), Error::::NoPermission); let w = Asset::::get(0).unwrap().destroy_witness(); - assert_noop!(Assets::destroy(Origin::signed(2), 0, w), Error::::NoPermission); + assert_noop!(Uniques::destroy(Origin::signed(2), 0, w), Error::::NoPermission); }); } @@ -332,19 +256,19 @@ fn transfer_owner_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 100); - assert_ok!(Assets::create(Origin::signed(1), 0, 1, 1)); + assert_ok!(Uniques::create(Origin::signed(1), 0, 1, 1)); assert_eq!(Balances::reserved_balance(&1), 1); - assert_ok!(Assets::transfer_ownership(Origin::signed(1), 0, 2)); + assert_ok!(Uniques::transfer_ownership(Origin::signed(1), 0, 2)); assert_eq!(Balances::reserved_balance(&2), 1); assert_eq!(Balances::reserved_balance(&1), 0); - assert_noop!(Assets::transfer_ownership(Origin::signed(1), 0, 1), Error::::NoPermission); + assert_noop!(Uniques::transfer_ownership(Origin::signed(1), 0, 1), Error::::NoPermission); // Set metadata now and make sure that deposit gets transferred back. - assert_ok!(Assets::set_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], 12)); - assert_ok!(Assets::transfer_ownership(Origin::signed(2), 0, 1)); + assert_ok!(Uniques::set_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], 12)); + assert_ok!(Uniques::transfer_ownership(Origin::signed(2), 0, 1)); assert_eq!(Balances::reserved_balance(&1), 22); assert_eq!(Balances::reserved_balance(&2), 0); }); @@ -353,54 +277,54 @@ fn transfer_owner_should_work() { #[test] fn set_team_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::set_team(Origin::signed(1), 0, 2, 3, 4)); - - assert_ok!(Assets::mint(Origin::signed(2), 0, 2, 100)); - assert_ok!(Assets::freeze(Origin::signed(4), 0, 2)); - assert_ok!(Assets::thaw(Origin::signed(3), 0, 2)); - assert_ok!(Assets::force_transfer(Origin::signed(3), 0, 2, 3, 100)); - assert_ok!(Assets::burn(Origin::signed(3), 0, 3, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::set_team(Origin::signed(1), 0, 2, 3, 4)); + + assert_ok!(Uniques::mint(Origin::signed(2), 0, 2, 100)); + assert_ok!(Uniques::freeze(Origin::signed(4), 0, 2)); + assert_ok!(Uniques::thaw(Origin::signed(3), 0, 2)); + assert_ok!(Uniques::force_transfer(Origin::signed(3), 0, 2, 3, 100)); + assert_ok!(Uniques::burn(Origin::signed(3), 0, 3, 100)); }); } #[test] fn transferring_to_frozen_account_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 2, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_eq!(Assets::balance(0, 2), 100); - assert_ok!(Assets::freeze(Origin::signed(1), 0, 2)); - assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Assets::balance(0, 2), 150); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 2, 100)); + assert_eq!(Uniques::balance(0, 1), 100); + assert_eq!(Uniques::balance(0, 2), 100); + assert_ok!(Uniques::freeze(Origin::signed(1), 0, 2)); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Uniques::balance(0, 2), 150); }); } #[test] fn transferring_amount_more_than_available_balance_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 50); - assert_ok!(Assets::burn(Origin::signed(1), 0, 1, u64::max_value())); - assert_eq!(Assets::balance(0, 1), 0); - assert_noop!(Assets::transfer(Origin::signed(1), 0, 1, 50), Error::::BalanceLow); - assert_noop!(Assets::transfer(Origin::signed(2), 0, 1, 51), Error::::BalanceLow); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Uniques::balance(0, 1), 100); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Uniques::balance(0, 1), 50); + assert_eq!(Uniques::balance(0, 2), 50); + assert_ok!(Uniques::burn(Origin::signed(1), 0, 1, u64::max_value())); + assert_eq!(Uniques::balance(0, 1), 0); + assert_noop!(Uniques::transfer(Origin::signed(1), 0, 1, 50), Error::::BalanceLow); + assert_noop!(Uniques::transfer(Origin::signed(2), 0, 1, 51), Error::::BalanceLow); }); } #[test] fn transferring_less_than_one_unit_is_fine() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 0)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Uniques::balance(0, 1), 100); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 0)); assert_eq!( last_event(), mock::Event::pallet_assets(crate::Event::Transferred(0, 1, 2, 0)), @@ -411,33 +335,33 @@ fn transferring_less_than_one_unit_is_fine() { #[test] fn transferring_more_units_than_total_supply_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 101), Error::::BalanceLow); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Uniques::balance(0, 1), 100); + assert_noop!(Uniques::transfer(Origin::signed(1), 0, 2, 101), Error::::BalanceLow); }); } #[test] fn burning_asset_balance_with_positive_balance_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::burn(Origin::signed(1), 0, 1, u64::max_value())); - assert_eq!(Assets::balance(0, 1), 0); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Uniques::balance(0, 1), 100); + assert_ok!(Uniques::burn(Origin::signed(1), 0, 1, u64::max_value())); + assert_eq!(Uniques::balance(0, 1), 0); }); } #[test] fn burning_asset_balance_with_zero_balance_does_nothing() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 2), 0); - assert_ok!(Assets::burn(Origin::signed(1), 0, 2, u64::max_value())); - assert_eq!(Assets::balance(0, 2), 0); - assert_eq!(Assets::total_supply(0), 100); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Uniques::balance(0, 2), 0); + assert_ok!(Uniques::burn(Origin::signed(1), 0, 2, u64::max_value())); + assert_eq!(Uniques::balance(0, 2), 0); + assert_eq!(Uniques::total_supply(0), 100); }); } @@ -446,48 +370,48 @@ fn set_metadata_should_work() { new_test_ext().execute_with(|| { // Cannot add metadata to unknown asset assert_noop!( - Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12), + Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12), Error::::Unknown, ); - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); // Cannot add metadata to unowned asset assert_noop!( - Assets::set_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], 12), + Uniques::set_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], 12), Error::::NoPermission, ); // Cannot add oversized metadata assert_noop!( - Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 100], vec![0u8; 10], 12), + Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 100], vec![0u8; 10], 12), Error::::BadMetadata, ); assert_noop!( - Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 100], 12), + Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 100], 12), Error::::BadMetadata, ); // Successfully add metadata and take deposit Balances::make_free_balance_be(&1, 30); - assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12)); assert_eq!(Balances::free_balance(&1), 9); // Update deposit - assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 5], 12)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 5], 12)); assert_eq!(Balances::free_balance(&1), 14); - assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 15], 12)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 15], 12)); assert_eq!(Balances::free_balance(&1), 4); // Cannot over-reserve assert_noop!( - Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 20], vec![0u8; 20], 12), + Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 20], vec![0u8; 20], 12), BalancesError::::InsufficientBalance, ); // Clear Metadata assert!(Metadata::::contains_key(0)); - assert_noop!(Assets::clear_metadata(Origin::signed(2), 0), Error::::NoPermission); - assert_noop!(Assets::clear_metadata(Origin::signed(1), 1), Error::::Unknown); - assert_ok!(Assets::clear_metadata(Origin::signed(1), 0)); + assert_noop!(Uniques::clear_metadata(Origin::signed(2), 0), Error::::NoPermission); + assert_noop!(Uniques::clear_metadata(Origin::signed(1), 1), Error::::Unknown); + assert_ok!(Uniques::clear_metadata(Origin::signed(1), 0)); assert!(!Metadata::::contains_key(0)); }); } @@ -495,37 +419,37 @@ fn set_metadata_should_work() { #[test] fn freezer_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 10)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 10)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Uniques::balance(0, 1), 100); // freeze 50 of it. set_frozen_balance(0, 1, 50); - assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 20)); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 20)); // cannot transfer another 21 away as this would take the non-frozen balance (30) to below // the minimum balance (10). - assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 21), Error::::BalanceLow); + assert_noop!(Uniques::transfer(Origin::signed(1), 0, 2, 21), Error::::BalanceLow); // create an approved transfer... Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::approve_transfer(Origin::signed(1), 0, 2, 50)); + assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); let e = Error::::BalanceLow; // ...but that wont work either: - assert_noop!(Assets::transfer_approved(Origin::signed(2), 0, 1, 2, 21), e); + assert_noop!(Uniques::transfer_approved(Origin::signed(2), 0, 1, 2, 21), e); // a force transfer won't work also. let e = Error::::BalanceLow; - assert_noop!(Assets::force_transfer(Origin::signed(1), 0, 1, 2, 21), e); + assert_noop!(Uniques::force_transfer(Origin::signed(1), 0, 1, 2, 21), e); // reduce it to only 49 frozen... set_frozen_balance(0, 1, 49); // ...and it's all good: - assert_ok!(Assets::force_transfer(Origin::signed(1), 0, 1, 2, 21)); + assert_ok!(Uniques::force_transfer(Origin::signed(1), 0, 1, 2, 21)); // and if we clear it, we can remove the account completely. clear_frozen_balance(0, 1); - assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); assert_eq!(hooks(), vec![Hook::Died(0, 1)]); }); } @@ -535,10 +459,10 @@ fn imbalances_should_work() { use frame_support::traits::tokens::fungibles::Balanced; new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - let imb = Assets::issue(0, 100); - assert_eq!(Assets::total_supply(0), 100); + let imb = Uniques::issue(0, 100); + assert_eq!(Uniques::total_supply(0), 100); assert_eq!(imb.peek(), 100); let (imb1, imb2) = imb.split(30); @@ -546,11 +470,11 @@ fn imbalances_should_work() { assert_eq!(imb2.peek(), 70); drop(imb2); - assert_eq!(Assets::total_supply(0), 30); + assert_eq!(Uniques::total_supply(0), 30); - assert!(Assets::resolve(&1, imb1).is_ok()); - assert_eq!(Assets::balance(0, 1), 30); - assert_eq!(Assets::total_supply(0), 30); + assert!(Uniques::resolve(&1, imb1).is_ok()); + assert_eq!(Uniques::balance(0, 1), 30); + assert_eq!(Uniques::total_supply(0), 30); }); } @@ -558,39 +482,39 @@ fn imbalances_should_work() { fn force_metadata_should_work() { new_test_ext().execute_with(|| { //force set metadata works - assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Assets::force_set_metadata(Origin::root(), 0, vec![0u8; 10], vec![0u8; 10], 8, false)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::force_set_metadata(Origin::root(), 0, vec![0u8; 10], vec![0u8; 10], 8, false)); assert!(Metadata::::contains_key(0)); //overwrites existing metadata let asset_original_metadata = Metadata::::get(0); - assert_ok!(Assets::force_set_metadata(Origin::root(), 0, vec![1u8; 10], vec![1u8; 10], 8, false)); + assert_ok!(Uniques::force_set_metadata(Origin::root(), 0, vec![1u8; 10], vec![1u8; 10], 8, false)); assert_ne!(Metadata::::get(0), asset_original_metadata); //attempt to set metadata for non-existent asset class assert_noop!( - Assets::force_set_metadata(Origin::root(), 1, vec![0u8; 10], vec![0u8; 10], 8, false), + Uniques::force_set_metadata(Origin::root(), 1, vec![0u8; 10], vec![0u8; 10], 8, false), Error::::Unknown ); //string length limit check let limit = StringLimit::get() as usize; assert_noop!( - Assets::force_set_metadata(Origin::root(), 0, vec![0u8; limit + 1], vec![0u8; 10], 8, false), + Uniques::force_set_metadata(Origin::root(), 0, vec![0u8; limit + 1], vec![0u8; 10], 8, false), Error::::BadMetadata ); assert_noop!( - Assets::force_set_metadata(Origin::root(), 0, vec![0u8; 10], vec![0u8; limit + 1], 8, false), + Uniques::force_set_metadata(Origin::root(), 0, vec![0u8; 10], vec![0u8; limit + 1], 8, false), Error::::BadMetadata ); //force clear metadata works assert!(Metadata::::contains_key(0)); - assert_ok!(Assets::force_clear_metadata(Origin::root(), 0)); + assert_ok!(Uniques::force_clear_metadata(Origin::root(), 0)); assert!(!Metadata::::contains_key(0)); //Error handles clearing non-existent asset class - assert_noop!(Assets::force_clear_metadata(Origin::root(), 1), Error::::Unknown); + assert_noop!(Uniques::force_clear_metadata(Origin::root(), 1), Error::::Unknown); }); } @@ -599,36 +523,130 @@ fn force_asset_status_should_work(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 10); Balances::make_free_balance_be(&2, 10); - assert_ok!(Assets::create(Origin::signed(1), 0, 1, 30)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 50)); - assert_ok!(Assets::mint(Origin::signed(1), 0, 2, 150)); + assert_ok!(Uniques::create(Origin::signed(1), 0, 1, 30)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 50)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 2, 150)); - //force asset status to change min_balance > balance - assert_ok!(Assets::force_asset_status(Origin::root(), 0, 1, 1, 1, 1, 100, true, false)); - assert_eq!(Assets::balance(0, 1), 50); + //force asset status to change min_balance > balance + assert_ok!(Uniques::force_asset_status(Origin::root(), 0, 1, 1, 1, 1, 100, true, false)); + assert_eq!(Uniques::balance(0, 1), 50); //account can recieve assets for balance < min_balance - assert_ok!(Assets::transfer(Origin::signed(2), 0, 1, 1)); - assert_eq!(Assets::balance(0, 1), 51); + assert_ok!(Uniques::transfer(Origin::signed(2), 0, 1, 1)); + assert_eq!(Uniques::balance(0, 1), 51); //account on outbound transfer will cleanup for balance < min_balance - assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 1)); - assert_eq!(Assets::balance(0,1), 0); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 1)); + assert_eq!(Uniques::balance(0,1), 0); //won't create new account with balance below min_balance - assert_noop!(Assets::transfer(Origin::signed(2), 0, 3, 50), TokenError::BelowMinimum); + assert_noop!(Uniques::transfer(Origin::signed(2), 0, 3, 50), TokenError::BelowMinimum); //force asset status will not execute for non-existent class assert_noop!( - Assets::force_asset_status(Origin::root(), 1, 1, 1, 1, 1, 90, true, false), + Uniques::force_asset_status(Origin::root(), 1, 1, 1, 1, 1, 90, true, false), Error::::Unknown ); //account drains to completion when funds dip below min_balance - assert_ok!(Assets::force_asset_status(Origin::root(), 0, 1, 1, 1, 1, 110, true, false)); - assert_ok!(Assets::transfer(Origin::signed(2), 0, 1, 110)); - assert_eq!(Assets::balance(0, 1), 200); - assert_eq!(Assets::balance(0, 2), 0); - assert_eq!(Assets::total_supply(0), 200); + assert_ok!(Uniques::force_asset_status(Origin::root(), 0, 1, 1, 1, 1, 110, true, false)); + assert_ok!(Uniques::transfer(Origin::signed(2), 0, 1, 110)); + assert_eq!(Uniques::balance(0, 1), 200); + assert_eq!(Uniques::balance(0, 2), 0); + assert_eq!(Uniques::total_supply(0), 200); + }); +} + +#[test] +fn approval_lifecycle_works() { + new_test_ext().execute_with(|| { + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + Balances::make_free_balance_be(&1, 1); + assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Balances::reserved_balance(&1), 1); + assert_ok!(Uniques::transfer_approved(Origin::signed(2), 0, 1, 3, 40)); + assert_ok!(Uniques::cancel_approval(Origin::signed(1), 0, 2)); + assert_eq!(Uniques::balance(0, 1), 60); + assert_eq!(Uniques::balance(0, 3), 40); + assert_eq!(Balances::reserved_balance(&1), 0); + }); +} + +#[test] +fn approval_deposits_work() { + new_test_ext().execute_with(|| { + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + let e = BalancesError::::InsufficientBalance; + assert_noop!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50), e); + + Balances::make_free_balance_be(&1, 1); + assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Balances::reserved_balance(&1), 1); + + assert_ok!(Uniques::transfer_approved(Origin::signed(2), 0, 1, 3, 50)); + assert_eq!(Balances::reserved_balance(&1), 0); + + assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); + assert_ok!(Uniques::cancel_approval(Origin::signed(1), 0, 2)); + assert_eq!(Balances::reserved_balance(&1), 0); + }); +} + +#[test] +fn cannot_transfer_more_than_approved() { + new_test_ext().execute_with(|| { + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + Balances::make_free_balance_be(&1, 1); + assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); + let e = Error::::Unapproved; + assert_noop!(Uniques::transfer_approved(Origin::signed(2), 0, 1, 3, 51), e); + }); +} + +#[test] +fn cannot_transfer_more_than_exists() { + new_test_ext().execute_with(|| { + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + Balances::make_free_balance_be(&1, 1); + assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 101)); + let e = Error::::BalanceLow; + assert_noop!(Uniques::transfer_approved(Origin::signed(2), 0, 1, 3, 101), e); + }); +} + +#[test] +fn cancel_approval_works() { + new_test_ext().execute_with(|| { + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + Balances::make_free_balance_be(&1, 1); + assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); + assert_noop!(Uniques::cancel_approval(Origin::signed(1), 1, 2), Error::::Unknown); + assert_noop!(Uniques::cancel_approval(Origin::signed(2), 0, 2), Error::::Unknown); + assert_noop!(Uniques::cancel_approval(Origin::signed(1), 0, 3), Error::::Unknown); + assert_ok!(Uniques::cancel_approval(Origin::signed(1), 0, 2)); + assert_noop!(Uniques::cancel_approval(Origin::signed(1), 0, 2), Error::::Unknown); + }); +} + +#[test] +fn force_cancel_approval_works() { + new_test_ext().execute_with(|| { + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + Balances::make_free_balance_be(&1, 1); + assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); + let e = Error::::NoPermission; + assert_noop!(Uniques::force_cancel_approval(Origin::signed(2), 0, 1, 2), e); + assert_noop!(Uniques::force_cancel_approval(Origin::signed(1), 1, 1, 2), Error::::Unknown); + assert_noop!(Uniques::force_cancel_approval(Origin::signed(1), 0, 2, 2), Error::::Unknown); + assert_noop!(Uniques::force_cancel_approval(Origin::signed(1), 0, 1, 3), Error::::Unknown); + assert_ok!(Uniques::force_cancel_approval(Origin::signed(1), 0, 1, 2)); + assert_noop!(Uniques::force_cancel_approval(Origin::signed(1), 0, 1, 2), Error::::Unknown); }); } +*/ From f5c9d1dafa672f6de303aab9d494232b6694bd40 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 19 May 2021 11:18:33 +0200 Subject: [PATCH 15/54] Remove impl_non_fungibles --- frame/uniques/src/impl_non_fungibles.rs | 156 ------------------------ frame/uniques/src/lib.rs | 1 - 2 files changed, 157 deletions(-) delete mode 100644 frame/uniques/src/impl_non_fungibles.rs diff --git a/frame/uniques/src/impl_non_fungibles.rs b/frame/uniques/src/impl_non_fungibles.rs deleted file mode 100644 index d0ab13072a88d..0000000000000 --- a/frame/uniques/src/impl_non_fungibles.rs +++ /dev/null @@ -1,156 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2017-2021 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 super::*; - -impl, I: 'static> fungibles::Inspect<::AccountId> for Pallet { - type AssetId = T::AssetId; - type Balance = T::Balance; - - fn total_issuance(asset: Self::AssetId) -> Self::Balance { - Asset::::get(asset) - .map(|x| x.supply) - .unwrap_or_else(Zero::zero) - } - - fn minimum_balance(asset: Self::AssetId) -> Self::Balance { - Asset::::get(asset) - .map(|x| x.min_balance) - .unwrap_or_else(Zero::zero) - } - - fn balance(asset: Self::AssetId, who: &::AccountId) -> Self::Balance { - Pallet::::balance(asset, who) - } - - fn reducible_balance( - asset: Self::AssetId, - who: &::AccountId, - keep_alive: bool, - ) -> Self::Balance { - Pallet::::reducible_balance(asset, who, keep_alive).unwrap_or(Zero::zero()) - } - - fn can_deposit( - asset: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - ) -> DepositConsequence { - Pallet::::can_increase(asset, who, amount) - } - - fn can_withdraw( - asset: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - ) -> WithdrawConsequence { - Pallet::::can_decrease(asset, who, amount, false) - } -} - -impl, I: 'static> fungibles::Mutate<::AccountId> for Pallet { - fn mint_into( - asset: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - Self::do_mint(asset, who, amount, None) - } - - fn burn_from( - asset: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - ) -> Result { - let f = DebitFlags { - keep_alive: false, - best_effort: false, - }; - Self::do_burn(asset, who, amount, None, f) - } - - fn slash( - asset: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - ) -> Result { - let f = DebitFlags { - keep_alive: false, - best_effort: true, - }; - Self::do_burn(asset, who, amount, None, f) - } -} - -impl, I: 'static> fungibles::Transfer for Pallet { - fn transfer( - asset: Self::AssetId, - source: &T::AccountId, - dest: &T::AccountId, - amount: T::Balance, - keep_alive: bool, - ) -> Result { - let f = TransferFlags { - keep_alive, - best_effort: false, - burn_dust: false - }; - Self::do_transfer(asset, source, dest, amount, None, f) - } -} - -impl, I: 'static> fungibles::Unbalanced for Pallet { - fn set_balance(_: Self::AssetId, _: &T::AccountId, _: Self::Balance) -> DispatchResult { - unreachable!("set_balance is not used if other functions are impl'd"); - } - fn set_total_issuance(id: T::AssetId, amount: Self::Balance) { - Asset::::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) - -> Result - { - let f = DebitFlags { keep_alive: false, best_effort: false }; - Self::decrease_balance(asset, who, amount, f, |_, _| Ok(())) - } - fn decrease_balance_at_most(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance) - -> Self::Balance - { - let f = DebitFlags { keep_alive: false, best_effort: true }; - Self::decrease_balance(asset, who, amount, f, |_, _| Ok(())) - .unwrap_or(Zero::zero()) - } - fn increase_balance(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance) - -> Result - { - Self::increase_balance(asset, who, amount, |_| Ok(()))?; - Ok(amount) - } - fn increase_balance_at_most(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance) - -> Self::Balance - { - match Self::increase_balance(asset, who, amount, |_| Ok(())) { - Ok(()) => amount, - Err(_) => Zero::zero(), - } - } -} diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 81e53576aae02..ca86b0419ce38 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -37,7 +37,6 @@ pub mod mock; #[cfg(test)] mod tests; -// TODO: mod impl_non_fungibles; mod functions; mod types; pub use types::*; From 7874135d2b40fefe760cd0e61b9b768c50fb97e6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 19 May 2021 11:19:32 +0200 Subject: [PATCH 16/54] Docs --- frame/uniques/Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/frame/uniques/Cargo.toml b/frame/uniques/Cargo.toml index cbf6cd5008463..f007744dc64a2 100644 --- a/frame/uniques/Cargo.toml +++ b/frame/uniques/Cargo.toml @@ -16,11 +16,8 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-std = { version = "3.0.0", default-features = false, path = "../../primitives/std" } sp-core = { version = "3.0.0", default-features = false, path = "../../primitives/core" } -# Needed for various traits. In our case, `OnFinalize`. sp-runtime = { version = "3.0.0", default-features = false, path = "../../primitives/runtime" } -# Needed for type-safe access to storage DB. frame-support = { version = "3.0.0", default-features = false, path = "../support" } -# `system` module provides us with all sorts of useful stuff and macros depend on it being around. frame-system = { version = "3.0.0", default-features = false, path = "../system" } frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } From 6a231a8cfd5a3a5b7c5efc0be8ee7b235a266b43 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Wed, 19 May 2021 11:21:11 +0200 Subject: [PATCH 17/54] Update frame/uniques/src/lib.rs Co-authored-by: Shawn Tabrizi --- frame/uniques/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index ca86b0419ce38..701babc0701f1 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -160,7 +160,7 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] #[pallet::metadata( T::AccountId = "AccountId", - T::ClassId = "AssetId", + T::ClassId = "ClassId", T::InstanceId = "InstanceId", )] pub enum Event, I: 'static = ()> { From 4699b23c95f3bd38a4937d587226b46d4e17925c Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Wed, 19 May 2021 11:23:32 +0200 Subject: [PATCH 18/54] Update frame/uniques/src/lib.rs Co-authored-by: Shawn Tabrizi --- frame/uniques/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 701babc0701f1..b8856f0cfdc50 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -1136,7 +1136,7 @@ pub mod pallet { is_frozen, }); - Self::deposit_event(Event::ClassMetadataSet(class, name, info, false)); + Self::deposit_event(Event::ClassMetadataSet(class, name, info, is_frozen)); Ok(()) }) } From fb6ea0353e567e2f626e6830bbcd18c4365887a4 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Wed, 19 May 2021 11:23:38 +0200 Subject: [PATCH 19/54] Update frame/uniques/src/lib.rs Co-authored-by: Shawn Tabrizi --- frame/uniques/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index b8856f0cfdc50..a6e1d2969867d 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -1022,7 +1022,7 @@ pub mod pallet { }); Class::::insert(&class, &class_details); - Self::deposit_event(Event::MetadataSet(class, name, info, false)); + Self::deposit_event(Event::MetadataSet(class, name, info, is_frozen)); Ok(()) }) } From 87cc1d899c59d81c2bc5d161eca843522ece1b72 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Wed, 19 May 2021 11:23:48 +0200 Subject: [PATCH 20/54] Update frame/uniques/src/lib.rs Co-authored-by: Shawn Tabrizi --- frame/uniques/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index a6e1d2969867d..82a48880c3273 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -1011,7 +1011,7 @@ pub mod pallet { } else { old_deposit }; - class_details.metadata_deposit =class_details.metadata_deposit + class_details.metadata_deposit = class_details.metadata_deposit .saturating_add(deposit); *metadata = Some(InstanceMetadata { From 8e8b4df97680c4ed7d7a7d91db4ea5928e13f6d9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 19 May 2021 12:23:15 +0200 Subject: [PATCH 21/54] Reserve, don't transfer. --- frame/uniques/src/functions.rs | 93 ---------------------------------- frame/uniques/src/lib.rs | 64 ++++++++++++++--------- frame/uniques/src/types.rs | 8 ++- 3 files changed, 42 insertions(+), 123 deletions(-) diff --git a/frame/uniques/src/functions.rs b/frame/uniques/src/functions.rs index d9dad4b9e900a..93507e80775cd 100644 --- a/frame/uniques/src/functions.rs +++ b/frame/uniques/src/functions.rs @@ -17,96 +17,3 @@ //! Functions for the Assets pallet. -use super::*; -use sp_runtime::traits::Zero; -use sp_runtime::DispatchError; -use frame_support::traits::{PalletInfoAccess, ExistenceRequirement::AllowDeath, Get}; - -/// Conversion trait to allow a pallet instance to be converted into an account ID. -/// -/// The same account ID will be returned as long as the pallet remains with the same name and index -/// within the runtime construction. -pub trait PalletIntoAccount { - /// Convert into an account ID. This is infallible. - fn into_account() -> AccountId { Self::into_sub_account(&()) } - - /// Convert this value amalgamated with the a secondary "sub" value into an account ID. This is - /// infallible. - /// - /// NOTE: The account IDs from this and from `into_account` are *not* guaranteed to be distinct - /// for any given value of `self`, nor are different invocations to this with different types - /// `T`. For example, the following will all encode to the same account ID value: - /// - `self.into_sub_account(0u32)` - /// - `self.into_sub_account(vec![0u8; 0])` - /// - `self.into_account()` - fn into_sub_account(sub: S) -> AccountId; -} -impl PalletIntoAccount for T { - fn into_sub_account(sub: S) -> AccountId { - sp_runtime::traits::AccountIdConversion::into_sub_account(&PalletId::new::(), sub) - } -} - -#[derive(Encode, Decode)] -pub struct PalletId(u32, sp_runtime::RuntimeString); -impl sp_core::TypeId for PalletId { - const TYPE_ID: [u8; 4] = *b"PALI"; -} -impl PalletId { - pub fn new() -> Self { - Self(T::index() as u32, T::name().into()) - } -} - -// The main implementation block for the module. -impl, I: 'static> Pallet { - // Public immutables - - pub(super) fn new_instance( - owner: T::AccountId, - maybe_override_deposit: Option>, - class_details: &mut ClassDetails>, - ) -> Result>, DispatchError> { - let instances = class_details.instances.checked_add(1).ok_or(ArithmeticError::Overflow)?; - let deposit = if class_details.free_holding { - Zero::zero() - } else { - maybe_override_deposit.unwrap_or_else(T::InstanceDeposit::get) - }; - if deposit.is_zero() { - class_details.free_holds += 1; - } else { - T::Currency::transfer( - &class_details.owner, - &Self::into_account(), - deposit, - AllowDeath, - )?; - } - class_details.instances = instances; - Ok(InstanceDetails { - owner, - approved: None, - is_frozen: false, - deposit, - }) - } - - pub(super) fn dead_instance( - instance_details: &mut InstanceDetails>, - class_details: &mut ClassDetails>, - ) { - if !instance_details.deposit.is_zero() { - // Return the deposit. - let ok = T::Currency::transfer( - &Self::into_account(), - &class_details.owner, - instance_details.deposit, - AllowDeath, - ).is_ok(); - debug_assert!(ok, "Unable to return deposited funds. Where did they go?"); - instance_details.deposit = Zero::zero(); - } - class_details.instances = class_details.instances.saturating_sub(1); - } -} diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 82a48880c3273..f848049f12c79 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -281,8 +281,7 @@ pub mod pallet { issuer: admin.clone(), admin: admin.clone(), freezer: admin.clone(), - deposit, - metadata_deposit: Zero::zero(), + total_deposit: deposit, free_holding: false, instances: 0, free_holds: 0, @@ -328,8 +327,7 @@ pub mod pallet { issuer: owner.clone(), admin: owner.clone(), freezer: owner.clone(), - deposit: Zero::zero(), - metadata_deposit: Zero::zero(), + total_deposit: Zero::zero(), free_holding, instances: 0, free_holds: 0, @@ -378,16 +376,9 @@ pub mod pallet { for (instance, mut details) in Asset::::drain_prefix(&class) { Account::::remove((&details.owner, &class, &instance)); InstanceMetadataOf::::remove(&class, &instance); - Self::dead_instance(&mut details, &mut class_details); } - debug_assert_eq!(class_details.instances, 0); - debug_assert_eq!(class_details.free_holds, 0); - ClassMetadataOf::::remove(&class); - T::Currency::unreserve( - &class_details.owner, - class_details.deposit.saturating_add(class_details.metadata_deposit), - ); + T::Currency::unreserve(&class_details.owner, class_details.total_deposit); Self::deposit_event(Event::Destroyed(class)); @@ -412,23 +403,38 @@ pub mod pallet { origin: OriginFor, #[pallet::compact] class: T::ClassId, #[pallet::compact] instance: T::InstanceId, - beneficiary: ::Source, + owner: ::Source, ) -> DispatchResult { let origin = ensure_signed(origin)?; - let beneficiary = T::Lookup::lookup(beneficiary)?; + let owner = T::Lookup::lookup(owner)?; ensure!(!Asset::::contains_key(class, instance), Error::::AlreadyExists); Class::::try_mutate(&class, |maybe_class_details| -> DispatchResult { let class_details = maybe_class_details.as_mut().ok_or(Error::::Unknown)?; ensure!(class_details.issuer == origin, Error::::NoPermission); - let details = Self::new_instance(beneficiary.clone(), None, class_details)?; + + let instances = class_details.instances.checked_add(1) + .ok_or(ArithmeticError::Overflow)?; + class_details.instances = instances; + + let deposit = if class_details.free_holding { + class_details.free_holds += 1; + Zero::zero() + } else { + let deposit = T::InstanceDeposit::get(); + T::Currency::reserve(&class_details.owner, deposit)?; + class_details.total_deposit += deposit; + deposit + }; + + let details = InstanceDetails { owner, approved: None, is_frozen: false, deposit}; Asset::::insert(&class, &instance, details); Ok(()) })?; - Account::::insert((&beneficiary, &class, &instance), ()); + Account::::insert((&owner, &class, &instance), ()); - Self::deposit_event(Event::Issued(class, instance, beneficiary)); + Self::deposit_event(Event::Issued(class, instance, owner)); Ok(()) } @@ -462,10 +468,18 @@ pub mod pallet { let is_permitted = class_details.admin == origin || details.owner == origin; ensure!(is_permitted, Error::::NoPermission); ensure!(check_owner.map_or(true, |o| o == details.owner), Error::::WrongOwner); - Self::dead_instance(&mut details, class_details); + + if !details.deposit.is_zero() { + // Return the deposit. + T::Currency::unreserve(&class_details.owner, details.deposit); + class_details.total_deposit = class_details.total_deposit + .saturating_sub(details.deposit); + } + class_details.instances = class_details.instances.saturating_sub(1); + if let Some(metadata) = InstanceMetadataOf::::take(&class, &instance) { // Remove instance metadata - class_details.metadata_deposit = class_details.metadata_deposit + class_details.total_deposit = class_details.total_deposit .saturating_sub(metadata.deposit); T::Currency::unreserve(&class_details.owner, metadata.deposit); } @@ -696,7 +710,7 @@ pub mod pallet { return Ok(()); } - let deposit = details.deposit + details.metadata_deposit; + let deposit = details.deposit + details.total_deposit; // Move the deposit to the new owner. T::Currency::repatriate_reserved(&details.owner, &owner, deposit, Reserved)?; @@ -994,7 +1008,7 @@ pub mod pallet { ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); - class_details.metadata_deposit = class_details.metadata_deposit + class_details.total_deposit = class_details.total_deposit .saturating_sub(old_deposit); let deposit = if let Some(owner) = maybe_check_owner { let deposit = T::MetadataDepositPerByte::get() @@ -1011,7 +1025,7 @@ pub mod pallet { } else { old_deposit }; - class_details.metadata_deposit = class_details.metadata_deposit + class_details.total_deposit = class_details.total_deposit .saturating_add(deposit); *metadata = Some(InstanceMetadata { @@ -1062,7 +1076,7 @@ pub mod pallet { let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; T::Currency::unreserve(&class_details.owner, deposit); - class_details.metadata_deposit = class_details.metadata_deposit.saturating_sub(deposit); + class_details.total_deposit = class_details.total_deposit.saturating_sub(deposit); Class::::insert(&class, &class_details); Self::deposit_event(Event::MetadataCleared(class)); @@ -1110,7 +1124,7 @@ pub mod pallet { ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); - details.metadata_deposit = details.metadata_deposit.saturating_sub(old_deposit); + details.total_deposit = details.total_deposit.saturating_sub(old_deposit); let deposit = if let Some(owner) = maybe_check_owner { let deposit = T::MetadataDepositPerByte::get() .saturating_mul(((name.len() + info.len()) as u32).into()) @@ -1125,7 +1139,7 @@ pub mod pallet { } else { old_deposit }; - details.metadata_deposit = details.metadata_deposit.saturating_add(deposit); + details.total_deposit = details.total_deposit.saturating_add(deposit); Class::::insert(&class, details); diff --git a/frame/uniques/src/types.rs b/frame/uniques/src/types.rs index 519521fa0d8e7..a9e4bc8d36f68 100644 --- a/frame/uniques/src/types.rs +++ b/frame/uniques/src/types.rs @@ -35,11 +35,9 @@ pub struct ClassDetails< pub(super) admin: AccountId, /// Can freeze tokens. pub(super) freezer: AccountId, - /// The balance deposited for this asset. This pays for the data stored here. - pub(super) deposit: DepositBalance, - /// The total balance deposited for the all metadata associated with this asset (both class and - /// instances). - pub(super) metadata_deposit: DepositBalance, + /// The total balance deposited for the all storage associated with this asset class. Used by + /// `destroy`. + pub(super) total_deposit: DepositBalance, /// If `true`, then no deposit is needed to hold instances of this class. pub(super) free_holding: bool, /// The total number of outstanding unique instances of this asset class. From 01f9713e3089df78f06f83fce029133b552dc44d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 19 May 2021 12:33:00 +0200 Subject: [PATCH 22/54] Fixes --- frame/uniques/src/functions.rs | 19 ------------------- frame/uniques/src/lib.rs | 26 +++++++++++++------------- frame/uniques/src/tests.rs | 9 ++++----- 3 files changed, 17 insertions(+), 37 deletions(-) delete mode 100644 frame/uniques/src/functions.rs diff --git a/frame/uniques/src/functions.rs b/frame/uniques/src/functions.rs deleted file mode 100644 index 93507e80775cd..0000000000000 --- a/frame/uniques/src/functions.rs +++ /dev/null @@ -1,19 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2017-2021 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. - -//! Functions for the Assets pallet. - diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index f848049f12c79..81b5e223fddfe 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -37,7 +37,6 @@ pub mod mock; #[cfg(test)] mod tests; -mod functions; mod types; pub use types::*; @@ -366,14 +365,14 @@ pub mod pallet { Err(origin) => Some(ensure_signed(origin)?), }; Class::::try_mutate_exists(class, |maybe_details| { - let mut class_details = maybe_details.take().ok_or(Error::::Unknown)?; + let class_details = maybe_details.take().ok_or(Error::::Unknown)?; if let Some(check_owner) = maybe_check_owner { ensure!(class_details.owner == check_owner, Error::::NoPermission); } ensure!(class_details.instances == witness.instances, Error::::BadWitness); ensure!(class_details.free_holds == witness.free_holds, Error::::BadWitness); - for (instance, mut details) in Asset::::drain_prefix(&class) { + for (instance, details) in Asset::::drain_prefix(&class) { Account::::remove((&details.owner, &class, &instance)); InstanceMetadataOf::::remove(&class, &instance); } @@ -428,11 +427,12 @@ pub mod pallet { deposit }; + let owner = owner.clone(); + Account::::insert((&owner, &class, &instance), ()); let details = InstanceDetails { owner, approved: None, is_frozen: false, deposit}; Asset::::insert(&class, &instance, details); Ok(()) })?; - Account::::insert((&owner, &class, &instance), ()); Self::deposit_event(Event::Issued(class, instance, owner)); Ok(()) @@ -463,7 +463,7 @@ pub mod pallet { let owner = Class::::try_mutate(&class, |maybe_class_details| -> Result { let class_details = maybe_class_details.as_mut().ok_or(Error::::Unknown)?; - let mut details = Asset::::get(&class, &instance) + let details = Asset::::get(&class, &instance) .ok_or(Error::::Unknown)?; let is_permitted = class_details.admin == origin || details.owner == origin; ensure!(is_permitted, Error::::NoPermission); @@ -710,11 +710,13 @@ pub mod pallet { return Ok(()); } - let deposit = details.deposit + details.total_deposit; - // Move the deposit to the new owner. - T::Currency::repatriate_reserved(&details.owner, &owner, deposit, Reserved)?; - + T::Currency::repatriate_reserved( + &details.owner, + &owner, + details.total_deposit, + Reserved, + )?; details.owner = owner.clone(); Self::deposit_event(Event::OwnerChanged(class, owner)); @@ -1008,8 +1010,7 @@ pub mod pallet { ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); - class_details.total_deposit = class_details.total_deposit - .saturating_sub(old_deposit); + class_details.total_deposit = class_details.total_deposit.saturating_sub(old_deposit); let deposit = if let Some(owner) = maybe_check_owner { let deposit = T::MetadataDepositPerByte::get() .saturating_mul(((name.len() + info.len()) as u32).into()) @@ -1025,8 +1026,7 @@ pub mod pallet { } else { old_deposit }; - class_details.total_deposit = class_details.total_deposit - .saturating_add(deposit); + class_details.total_deposit = class_details.total_deposit.saturating_add(deposit); *metadata = Some(InstanceMetadata { deposit, diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index 571598c7a7fe1..57e9c1033d225 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -75,21 +75,20 @@ fn lifecycle_should_work() { assert!(ClassMetadataOf::::contains_key(0)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 10)); - assert_eq!(Balances::total_balance(&1), 99); + assert_eq!(Balances::reserved_balance(&1), 6); assert_ok!(Uniques::mint(Origin::signed(1), 0, 69, 20)); - assert_eq!(Balances::total_balance(&1), 98); + assert_eq!(Balances::reserved_balance(&1), 7); assert_eq!(assets(), vec![(10, 0, 42), (20, 0, 69)]); assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![42], vec![42], false)); - assert_eq!(Balances::reserved_balance(&1), 8); + assert_eq!(Balances::reserved_balance(&1), 10); assert!(InstanceMetadataOf::::contains_key(0, 42)); assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, vec![69], vec![69], false)); - assert_eq!(Balances::reserved_balance(&1), 11); + assert_eq!(Balances::reserved_balance(&1), 13); assert!(InstanceMetadataOf::::contains_key(0, 69)); let w = Class::::get(0).unwrap().destroy_witness(); assert_ok!(Uniques::destroy(Origin::signed(1), 0, w)); - assert_eq!(Balances::total_balance(&1), 100); assert_eq!(Balances::reserved_balance(&1), 0); assert!(!Class::::contains_key(0)); From 4bf6f54131c42c4fac516185ba3d5dac6ec17603 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 19 May 2021 13:33:32 +0200 Subject: [PATCH 23/54] Tests --- frame/uniques/src/lib.rs | 104 +++------------- frame/uniques/src/tests.rs | 236 +++++++++++++------------------------ 2 files changed, 100 insertions(+), 240 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 81b5e223fddfe..7c07112045fb8 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -195,10 +195,6 @@ pub mod pallet { /// cancelled by its `owner`. /// \[ clsss, instance, owner, delegate \] ApprovalCancelled(T::ClassId, T::InstanceId, T::AccountId, T::AccountId), - /// The `instance` of the asset `class` was transferred from `owner` to `destination` by - /// the approved `delegate`. - /// \[ class, instance, owner, delegate, destination \] - TransferredApproved(T::ClassId, T::InstanceId, T::AccountId, T::AccountId, T::AccountId), /// An asset `class` has had its attributes changed by the `Force` origin. /// \[ class \] AssetStatusChanged(T::ClassId), @@ -241,6 +237,13 @@ pub mod pallet { #[pallet::hooks] impl, I: 'static> Hooks> for Pallet {} + impl, I: 'static> Pallet { + /// Get the owner of the asset instance, if the asset exists. + pub fn owner(class: T::ClassId, instance: T::InstanceId) -> Option { + Asset::::get(class, instance).map(|i| i.owner) + } + } + #[pallet::call] impl, I: 'static> Pallet { /// Issue a new class of non-fungible assets from a public origin. @@ -496,8 +499,12 @@ pub mod pallet { /// Move an asset from the sender account to another. /// - /// Origin must be Signed and the signing account must be the owner of the asset `instance`. + /// Origin must be Signed and the signing account must be either: + /// - the Admin of the asset `class`; + /// - the Owner of the asset `instance`; + /// - the approved delegate for the asset `instance` (in this case, the approval is reset). /// + /// Arguments: /// - `class`: The class of the asset to be transferred. /// - `instance`: The instance of the asset to be transferred. /// - `dest`: The account to receive ownership of the asset. @@ -519,10 +526,13 @@ pub mod pallet { ensure!(!class_details.is_frozen, Error::::Frozen); let mut details = Asset::::get(&class, &instance).ok_or(Error::::Unknown)?; - ensure!(details.owner == origin, Error::::NoPermission); ensure!(!details.is_frozen, Error::::Frozen); + if details.owner != origin && class_details.admin != origin { + let approved = details.approved.take().map_or(false, |i| i == origin); + ensure!(approved, Error::::NoPermission); + } - Account::::remove((&origin, &class, &instance)); + Account::::remove((&details.owner, &class, &instance)); Account::::insert((&dest, &class, &instance), ()); details.owner = dest; Asset::::insert(&class, &instance, &details); @@ -532,44 +542,6 @@ pub mod pallet { Ok(()) } - /// Move some assets from one account to another. - /// - /// Origin must be Signed and the sender should be the Admin of the asset `class`. - /// - /// - `class`: The class of the asset to be transferred. - /// - `instance`: The instance of the asset to be transferred. - /// - `dest`: The account to receive ownership of the asset. - /// - /// Emits `Transferred`. - /// - /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::force_transfer())] - pub(super) fn force_transfer( - origin: OriginFor, - #[pallet::compact] class: T::ClassId, - #[pallet::compact] instance: T::InstanceId, - dest: ::Source, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let dest = T::Lookup::lookup(dest)?; - - let mut details = Asset::::get(&class, &instance).ok_or(Error::::Unknown)?; - let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; - ensure!(class_details.admin == origin, Error::::NoPermission); - - let source = details.owner; - details.owner = dest; - - Account::::remove((&source, &class, &instance)); - Account::::insert((&details.owner, &class, &instance), ()); - - Asset::::insert(&class, &instance, &details); - - Self::deposit_event(Event::Transferred(class, instance, source, details.owner)); - - Ok(()) - } - /// Disallow further unprivileged transfer of an asset instance. /// /// Origin must be Signed and the sender should be the Freezer of the asset `class`. @@ -877,48 +849,6 @@ pub mod pallet { Ok(()) } - /// Transfer some asset instance from a target account to a destination account by an - /// approved delegate. - /// - /// Origin must be Signed and there must be an approval in place for the signer on this - /// asset `instance`. - /// - /// - `class`: The class of the asset to transfer. - /// - `instance`: The instance of the asset to transfer. - /// - `dest`: The account to which ownership of the asset instance will be transferred. - /// - /// Emits `TransferredApproved` on success. - /// - /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::transfer_approved())] - pub(super) fn transfer_approved( - origin: OriginFor, - #[pallet::compact] class: T::ClassId, - #[pallet::compact] instance: T::InstanceId, - dest: ::Source, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let dest = T::Lookup::lookup(dest)?; - - let mut details = Asset::::get(&class, &instance).ok_or(Error::::Unknown)?; - ensure!(!details.is_frozen, Error::::Frozen); - let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; - ensure!(!class_details.is_frozen, Error::::Frozen); - ensure!(details.approved.as_ref() == Some(&origin), Error::::Unapproved); - - let source = details.owner; - details.owner = dest; - - Account::::remove((&source, &class, &instance)); - Account::::insert((&details.owner, &class, &instance), ()); - - Asset::::insert(&class, &instance, &details); - - Self::deposit_event(Event::TransferredApproved(class, instance, source, origin, details.owner)); - - Ok(()) - } - /// Alter the attributes of a given asset. /// /// Origin must be `ForceOrigin`. diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index 57e9c1033d225..e39a1e90ba2d1 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -26,23 +26,32 @@ fn last_event() -> mock::Event { } */ fn assets() -> Vec<(u64, u32, u32)> { - let mut r: Vec<_> = Account::::iter().map(|x| x.0).collect(); - r.sort(); - r -} -/* -fn assets_of(who: u64) -> Vec<(u32, u32)> { - let mut r: Vec<_> = Account::::iter_prefix((who,)).map(|x| x.0).collect(); + let mut r: Vec<_> = Account::::iter().map(|x| x.0).collect(); r.sort(); + let mut s: Vec<_> = Asset::::iter().map(|x| (x.2.owner, x.0, x.1)).collect(); + s.sort(); + assert_eq!(r, s); + for class in Asset::::iter() + .map(|x| x.0) + .scan(None, |s, item| if s.map_or(false, |last| last == item) { + *s = Some(item); + Some(None) + } else { + Some(Some(item)) + } + ).filter_map(|item| item) + { + let details = Class::::get(class).unwrap(); + let instances = Asset::::iter_prefix(class).count() as u32; + assert_eq!(details.instances, instances); + let free_holds = Asset::::iter_prefix(class) + .filter(|(_, item)| item.deposit.is_zero()) + .count() as u32; + assert_eq!(details.free_holds, free_holds); + } r } -fn assets_of_class(who: u64, class: u32) -> Vec { - let mut r: Vec<_> = Account::::iter_prefix((who, class)).map(|x| x.0).collect(); - r.sort(); - r -} -*/ #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { @@ -112,144 +121,65 @@ fn destroy_with_bad_witness_should_not_work() { assert_noop!(Uniques::destroy(Origin::signed(1), 0, w), Error::::BadWitness); }); } -/* -#[test] -fn non_providing_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false, 1)); - - Balances::make_free_balance_be(&0, 100); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 100)); - - // Cannot mint into account 2 since it doesn't (yet) exist... - assert_noop!(Uniques::mint(Origin::signed(1), 0, 1, 100), TokenError::CannotCreate); - // ...or transfer... - assert_noop!(Uniques::transfer(Origin::signed(0), 0, 1, 50), TokenError::CannotCreate); - // ...or force-transfer - assert_noop!(Uniques::force_transfer(Origin::signed(1), 0, 0, 1, 50), TokenError::CannotCreate); - - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - assert_ok!(Uniques::transfer(Origin::signed(0), 0, 1, 25)); - assert_ok!(Uniques::force_transfer(Origin::signed(1), 0, 0, 2, 25)); - }); -} #[test] -fn min_balance_should_work() { +fn mint_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 10)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Asset::::get(0).unwrap().accounts, 1); - - // Cannot create a new account with a balance that is below minimum... - assert_noop!(Uniques::mint(Origin::signed(1), 0, 2, 9), TokenError::BelowMinimum); - assert_noop!(Uniques::transfer(Origin::signed(1), 0, 2, 9), TokenError::BelowMinimum); - assert_noop!(Uniques::force_transfer(Origin::signed(1), 0, 1, 2, 9), TokenError::BelowMinimum); - - // When deducting from an account to below minimum, it should be reaped. - assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 91)); - assert!(Uniques::balance(0, 1).is_zero()); - assert_eq!(Uniques::balance(0, 2), 100); - assert_eq!(Asset::::get(0).unwrap().accounts, 1); - - assert_ok!(Uniques::force_transfer(Origin::signed(1), 0, 2, 1, 91)); - assert!(Uniques::balance(0, 2).is_zero()); - assert_eq!(Uniques::balance(0, 1), 100); - assert_eq!(Asset::::get(0).unwrap().accounts, 1); - - assert_ok!(Uniques::burn(Origin::signed(1), 0, 1, 91)); - assert!(Uniques::balance(0, 1).is_zero()); - assert_eq!(Asset::::get(0).unwrap().accounts, 0); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); + assert_eq!(Uniques::owner(0, 42).unwrap(), 1); + assert_eq!(assets(), vec![(1, 0, 42)]); }); } #[test] -fn querying_total_supply_should_work() { +fn transfer_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Uniques::balance(0, 1), 100); - assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Uniques::balance(0, 1), 50); - assert_eq!(Uniques::balance(0, 2), 50); - assert_ok!(Uniques::transfer(Origin::signed(2), 0, 3, 31)); - assert_eq!(Uniques::balance(0, 1), 50); - assert_eq!(Uniques::balance(0, 2), 19); - assert_eq!(Uniques::balance(0, 3), 31); - assert_ok!(Uniques::burn(Origin::signed(1), 0, 3, u64::max_value())); - assert_eq!(Uniques::total_supply(0), 69); - }); -} + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 2)); -#[test] -fn transferring_amount_below_available_balance_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Uniques::balance(0, 1), 100); - assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Uniques::balance(0, 1), 50); - assert_eq!(Uniques::balance(0, 2), 50); - }); -} + assert_ok!(Uniques::transfer(Origin::signed(2), 0, 42, 3)); + assert_eq!(assets(), vec![(3, 0, 42)]); + assert_noop!(Uniques::transfer(Origin::signed(2), 0, 42, 4), Error::::NoPermission); -#[test] -fn transferring_enough_to_kill_source_when_keep_alive_should_fail() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 10)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Uniques::balance(0, 1), 100); - assert_noop!(Uniques::transfer_keep_alive(Origin::signed(1), 0, 2, 91), Error::::BalanceLow); - assert_ok!(Uniques::transfer_keep_alive(Origin::signed(1), 0, 2, 90)); - assert_eq!(Uniques::balance(0, 1), 10); - assert_eq!(Uniques::balance(0, 2), 90); + assert_ok!(Uniques::approve_transfer(Origin::signed(3), 0, 42, 2)); + assert_ok!(Uniques::transfer(Origin::signed(2), 0, 42, 4)); }); } #[test] -fn transferring_frozen_user_should_not_work() { +fn freezing_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Uniques::balance(0, 1), 100); - assert_ok!(Uniques::freeze(Origin::signed(1), 0, 1)); - assert_noop!(Uniques::transfer(Origin::signed(1), 0, 2, 50), Error::::Frozen); - assert_ok!(Uniques::thaw(Origin::signed(1), 0, 1)); - assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); - }); -} + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); + assert_ok!(Uniques::freeze(Origin::signed(1), 0, 42)); + assert_noop!(Uniques::transfer(Origin::signed(1), 0, 42, 2), Error::::Frozen); -#[test] -fn transferring_frozen_asset_should_not_work() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); - assert_eq!(Uniques::balance(0, 1), 100); - assert_ok!(Uniques::freeze_asset(Origin::signed(1), 0)); - assert_noop!(Uniques::transfer(Origin::signed(1), 0, 2, 50), Error::::Frozen); - assert_ok!(Uniques::thaw_asset(Origin::signed(1), 0)); - assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); + assert_ok!(Uniques::thaw(Origin::signed(1), 0, 42)); + assert_ok!(Uniques::freeze_class(Origin::signed(1), 0)); + assert_noop!(Uniques::transfer(Origin::signed(1), 0, 42, 2), Error::::Frozen); + + assert_ok!(Uniques::thaw_class(Origin::signed(1), 0)); + assert_ok!(Uniques::transfer(Origin::signed(1), 0, 42, 2)); }); } #[test] fn origin_guards_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); assert_noop!(Uniques::transfer_ownership(Origin::signed(2), 0, 2), Error::::NoPermission); assert_noop!(Uniques::set_team(Origin::signed(2), 0, 2, 2, 2), Error::::NoPermission); - assert_noop!(Uniques::freeze(Origin::signed(2), 0, 1), Error::::NoPermission); - assert_noop!(Uniques::thaw(Origin::signed(2), 0, 2), Error::::NoPermission); - assert_noop!(Uniques::mint(Origin::signed(2), 0, 2, 100), Error::::NoPermission); - assert_noop!(Uniques::burn(Origin::signed(2), 0, 1, 100), Error::::NoPermission); - assert_noop!(Uniques::force_transfer(Origin::signed(2), 0, 1, 2, 100), Error::::NoPermission); - let w = Asset::::get(0).unwrap().destroy_witness(); + assert_noop!(Uniques::freeze(Origin::signed(2), 0, 42), Error::::NoPermission); + assert_noop!(Uniques::thaw(Origin::signed(2), 0, 42), Error::::NoPermission); + assert_noop!(Uniques::mint(Origin::signed(2), 0, 69, 2), Error::::NoPermission); + assert_noop!(Uniques::burn(Origin::signed(2), 0, 42, None), Error::::NoPermission); + let w = Class::::get(0).unwrap().destroy_witness(); assert_noop!(Uniques::destroy(Origin::signed(2), 0, w), Error::::NoPermission); }); } - +/* #[test] fn transfer_owner_should_work() { new_test_ext().execute_with(|| { @@ -276,7 +206,7 @@ fn transfer_owner_should_work() { #[test] fn set_team_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); assert_ok!(Uniques::set_team(Origin::signed(1), 0, 2, 3, 4)); assert_ok!(Uniques::mint(Origin::signed(2), 0, 2, 100)); @@ -290,8 +220,8 @@ fn set_team_should_work() { #[test] fn transferring_to_frozen_account_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 2, 100)); assert_eq!(Uniques::balance(0, 1), 100); assert_eq!(Uniques::balance(0, 2), 100); @@ -304,8 +234,8 @@ fn transferring_to_frozen_account_should_work() { #[test] fn transferring_amount_more_than_available_balance_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); assert_eq!(Uniques::balance(0, 1), 100); assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); assert_eq!(Uniques::balance(0, 1), 50); @@ -320,8 +250,8 @@ fn transferring_amount_more_than_available_balance_should_not_work() { #[test] fn transferring_less_than_one_unit_is_fine() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); assert_eq!(Uniques::balance(0, 1), 100); assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 0)); assert_eq!( @@ -334,8 +264,8 @@ fn transferring_less_than_one_unit_is_fine() { #[test] fn transferring_more_units_than_total_supply_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); assert_eq!(Uniques::balance(0, 1), 100); assert_noop!(Uniques::transfer(Origin::signed(1), 0, 2, 101), Error::::BalanceLow); }); @@ -344,8 +274,8 @@ fn transferring_more_units_than_total_supply_should_not_work() { #[test] fn burning_asset_balance_with_positive_balance_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); assert_eq!(Uniques::balance(0, 1), 100); assert_ok!(Uniques::burn(Origin::signed(1), 0, 1, u64::max_value())); assert_eq!(Uniques::balance(0, 1), 0); @@ -355,8 +285,8 @@ fn burning_asset_balance_with_positive_balance_should_work() { #[test] fn burning_asset_balance_with_zero_balance_does_nothing() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); assert_eq!(Uniques::balance(0, 2), 0); assert_ok!(Uniques::burn(Origin::signed(1), 0, 2, u64::max_value())); assert_eq!(Uniques::balance(0, 2), 0); @@ -372,7 +302,7 @@ fn set_metadata_should_work() { Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12), Error::::Unknown, ); - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); // Cannot add metadata to unowned asset assert_noop!( Uniques::set_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], 12), @@ -419,7 +349,7 @@ fn set_metadata_should_work() { fn freezer_should_work() { new_test_ext().execute_with(|| { assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 10)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); assert_eq!(Uniques::balance(0, 1), 100); @@ -458,7 +388,7 @@ fn imbalances_should_work() { use frame_support::traits::tokens::fungibles::Balanced; new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); let imb = Uniques::issue(0, 100); assert_eq!(Uniques::total_supply(0), 100); @@ -481,7 +411,7 @@ fn imbalances_should_work() { fn force_metadata_should_work() { new_test_ext().execute_with(|| { //force set metadata works - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); assert_ok!(Uniques::force_set_metadata(Origin::root(), 0, vec![0u8; 10], vec![0u8; 10], 8, false)); assert!(Metadata::::contains_key(0)); @@ -559,8 +489,8 @@ fn force_asset_status_should_work(){ #[test] fn approval_lifecycle_works() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); Balances::make_free_balance_be(&1, 1); assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); assert_eq!(Balances::reserved_balance(&1), 1); @@ -575,8 +505,8 @@ fn approval_lifecycle_works() { #[test] fn approval_deposits_work() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); let e = BalancesError::::InsufficientBalance; assert_noop!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50), e); @@ -596,8 +526,8 @@ fn approval_deposits_work() { #[test] fn cannot_transfer_more_than_approved() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); Balances::make_free_balance_be(&1, 1); assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); let e = Error::::Unapproved; @@ -608,8 +538,8 @@ fn cannot_transfer_more_than_approved() { #[test] fn cannot_transfer_more_than_exists() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); Balances::make_free_balance_be(&1, 1); assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 101)); let e = Error::::BalanceLow; @@ -620,8 +550,8 @@ fn cannot_transfer_more_than_exists() { #[test] fn cancel_approval_works() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); Balances::make_free_balance_be(&1, 1); assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); assert_noop!(Uniques::cancel_approval(Origin::signed(1), 1, 2), Error::::Unknown); @@ -635,8 +565,8 @@ fn cancel_approval_works() { #[test] fn force_cancel_approval_works() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); Balances::make_free_balance_be(&1, 1); assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); let e = Error::::NoPermission; From 821de3d99f03496886b557976a6cbb1d88709fc9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 19 May 2021 15:06:37 +0200 Subject: [PATCH 24/54] Tests --- frame/uniques/src/lib.rs | 3 + frame/uniques/src/tests.rs | 330 ++++++++++++------------------------- frame/uniques/src/types.rs | 1 + 3 files changed, 110 insertions(+), 224 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 7c07112045fb8..2a3b7f1ca0d96 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -935,6 +935,8 @@ pub mod pallet { ensure!(check_owner == &class_details.owner, Error::::NoPermission); } + ensure!(Asset::::contains_key(&class, &instance), Error::::Unknown); + InstanceMetadataOf::::try_mutate_exists(class, instance, |metadata| { let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); @@ -1043,6 +1045,7 @@ pub mod pallet { .or_else(|origin| ensure_signed(origin).map(Some))?; ensure!(name.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + ensure!(info.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); let mut details = Class::::get(&class).ok_or(Error::::Unknown)?; if let Some(check_owner) = &maybe_check_owner { diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index e39a1e90ba2d1..9b0d8b6f8cdd3 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -20,6 +20,7 @@ use super::*; use crate::mock::*; use frame_support::{assert_ok, assert_noop, traits::Currency}; +use pallet_balances::Error as BalancesError; /* fn last_event() -> mock::Event { frame_system::Pallet::::events().pop().expect("Event expected").event @@ -179,27 +180,31 @@ fn origin_guards_should_work() { assert_noop!(Uniques::destroy(Origin::signed(2), 0, w), Error::::NoPermission); }); } -/* + #[test] fn transfer_owner_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 100); - assert_ok!(Uniques::create(Origin::signed(1), 0, 1, 1)); - - assert_eq!(Balances::reserved_balance(&1), 1); - + Balances::make_free_balance_be(&3, 100); + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::transfer_ownership(Origin::signed(1), 0, 2)); - assert_eq!(Balances::reserved_balance(&2), 1); + assert_eq!(Balances::total_balance(&1), 98); + assert_eq!(Balances::total_balance(&2), 102); assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&2), 2); assert_noop!(Uniques::transfer_ownership(Origin::signed(1), 0, 1), Error::::NoPermission); - // Set metadata now and make sure that deposit gets transferred back. - assert_ok!(Uniques::set_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], 12)); - assert_ok!(Uniques::transfer_ownership(Origin::signed(2), 0, 1)); - assert_eq!(Balances::reserved_balance(&1), 22); + // Mint and set metadata now and make sure that deposit gets transferred back. + assert_ok!(Uniques::set_class_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], false)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); + assert_ok!(Uniques::set_metadata(Origin::signed(2), 0, 42, vec![0u8; 10], vec![0u8; 10], false)); + assert_ok!(Uniques::transfer_ownership(Origin::signed(2), 0, 3)); + assert_eq!(Balances::total_balance(&2), 57); + assert_eq!(Balances::total_balance(&3), 145); assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(Balances::reserved_balance(&3), 45); }); } @@ -209,244 +214,145 @@ fn set_team_should_work() { assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); assert_ok!(Uniques::set_team(Origin::signed(1), 0, 2, 3, 4)); - assert_ok!(Uniques::mint(Origin::signed(2), 0, 2, 100)); - assert_ok!(Uniques::freeze(Origin::signed(4), 0, 2)); - assert_ok!(Uniques::thaw(Origin::signed(3), 0, 2)); - assert_ok!(Uniques::force_transfer(Origin::signed(3), 0, 2, 3, 100)); - assert_ok!(Uniques::burn(Origin::signed(3), 0, 3, 100)); - }); -} - -#[test] -fn transferring_to_frozen_account_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 2, 100)); - assert_eq!(Uniques::balance(0, 1), 100); - assert_eq!(Uniques::balance(0, 2), 100); - assert_ok!(Uniques::freeze(Origin::signed(1), 0, 2)); - assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Uniques::balance(0, 2), 150); - }); -} - -#[test] -fn transferring_amount_more_than_available_balance_should_not_work() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - assert_eq!(Uniques::balance(0, 1), 100); - assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Uniques::balance(0, 1), 50); - assert_eq!(Uniques::balance(0, 2), 50); - assert_ok!(Uniques::burn(Origin::signed(1), 0, 1, u64::max_value())); - assert_eq!(Uniques::balance(0, 1), 0); - assert_noop!(Uniques::transfer(Origin::signed(1), 0, 1, 50), Error::::BalanceLow); - assert_noop!(Uniques::transfer(Origin::signed(2), 0, 1, 51), Error::::BalanceLow); - }); -} - -#[test] -fn transferring_less_than_one_unit_is_fine() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - assert_eq!(Uniques::balance(0, 1), 100); - assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 0)); - assert_eq!( - last_event(), - mock::Event::pallet_assets(crate::Event::Transferred(0, 1, 2, 0)), - ); - }); -} - -#[test] -fn transferring_more_units_than_total_supply_should_not_work() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - assert_eq!(Uniques::balance(0, 1), 100); - assert_noop!(Uniques::transfer(Origin::signed(1), 0, 2, 101), Error::::BalanceLow); - }); -} - -#[test] -fn burning_asset_balance_with_positive_balance_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - assert_eq!(Uniques::balance(0, 1), 100); - assert_ok!(Uniques::burn(Origin::signed(1), 0, 1, u64::max_value())); - assert_eq!(Uniques::balance(0, 1), 0); - }); -} - -#[test] -fn burning_asset_balance_with_zero_balance_does_nothing() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - assert_eq!(Uniques::balance(0, 2), 0); - assert_ok!(Uniques::burn(Origin::signed(1), 0, 2, u64::max_value())); - assert_eq!(Uniques::balance(0, 2), 0); - assert_eq!(Uniques::total_supply(0), 100); + assert_ok!(Uniques::mint(Origin::signed(2), 0, 42, 2)); + assert_ok!(Uniques::freeze(Origin::signed(4), 0, 42)); + assert_ok!(Uniques::thaw(Origin::signed(3), 0, 42)); + assert_ok!(Uniques::transfer(Origin::signed(3), 0, 42, 3)); + assert_ok!(Uniques::burn(Origin::signed(3), 0, 42, None)); }); } #[test] -fn set_metadata_should_work() { +fn set_class_metadata_should_work() { new_test_ext().execute_with(|| { // Cannot add metadata to unknown asset assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12), - Error::::Unknown, - ); + Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], false), + Error::::Unknown, + ); assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); // Cannot add metadata to unowned asset assert_noop!( - Uniques::set_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], 12), - Error::::NoPermission, - ); + Uniques::set_class_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], false), + Error::::NoPermission, + ); // Cannot add oversized metadata assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 100], vec![0u8; 10], 12), - Error::::BadMetadata, - ); + Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 51], vec![0u8; 1], false), + Error::::BadMetadata, + ); assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 100], 12), - Error::::BadMetadata, - ); + Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 1], vec![0u8; 51], false), + Error::::BadMetadata, + ); // Successfully add metadata and take deposit Balances::make_free_balance_be(&1, 30); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], false)); assert_eq!(Balances::free_balance(&1), 9); + assert!(ClassMetadataOf::::contains_key(0)); + + // Force origin works, too. + assert_ok!(Uniques::set_class_metadata(Origin::root(), 0, vec![0u8; 9], vec![0u8; 9], false)); // Update deposit - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 5], 12)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 5], false)); assert_eq!(Balances::free_balance(&1), 14); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 15], 12)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 15], false)); assert_eq!(Balances::free_balance(&1), 4); // Cannot over-reserve assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, vec![0u8; 20], vec![0u8; 20], 12), - BalancesError::::InsufficientBalance, - ); + Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 20], vec![0u8; 20], false), + BalancesError::::InsufficientBalance, + ); + + // Can't set or clear metadata once frozen + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 5], true)); + assert_noop!( + Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 5], false), + Error::::Frozen, + ); + assert_noop!(Uniques::clear_class_metadata(Origin::signed(1), 0), Error::::Frozen); // Clear Metadata - assert!(Metadata::::contains_key(0)); - assert_noop!(Uniques::clear_metadata(Origin::signed(2), 0), Error::::NoPermission); - assert_noop!(Uniques::clear_metadata(Origin::signed(1), 1), Error::::Unknown); - assert_ok!(Uniques::clear_metadata(Origin::signed(1), 0)); - assert!(!Metadata::::contains_key(0)); + assert_ok!(Uniques::set_class_metadata(Origin::root(), 0, vec![0u8; 10], vec![0u8; 5], false)); + assert_noop!(Uniques::clear_class_metadata(Origin::signed(2), 0), Error::::NoPermission); + assert_noop!(Uniques::clear_class_metadata(Origin::signed(1), 1), Error::::Unknown); + assert_ok!(Uniques::clear_class_metadata(Origin::signed(1), 0)); + assert!(!ClassMetadataOf::::contains_key(0)); }); } #[test] -fn freezer_should_work() { +fn set_instance_metadata_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true, 10)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - assert_eq!(Uniques::balance(0, 1), 100); - - - // freeze 50 of it. - set_frozen_balance(0, 1, 50); - - assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 20)); - // cannot transfer another 21 away as this would take the non-frozen balance (30) to below - // the minimum balance (10). - assert_noop!(Uniques::transfer(Origin::signed(1), 0, 2, 21), Error::::BalanceLow); - - // create an approved transfer... - Balances::make_free_balance_be(&1, 100); - assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); - let e = Error::::BalanceLow; - // ...but that wont work either: - assert_noop!(Uniques::transfer_approved(Origin::signed(2), 0, 1, 2, 21), e); - // a force transfer won't work also. - let e = Error::::BalanceLow; - assert_noop!(Uniques::force_transfer(Origin::signed(1), 0, 1, 2, 21), e); - - // reduce it to only 49 frozen... - set_frozen_balance(0, 1, 49); - // ...and it's all good: - assert_ok!(Uniques::force_transfer(Origin::signed(1), 0, 1, 2, 21)); - - // and if we clear it, we can remove the account completely. - clear_frozen_balance(0, 1); - assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(hooks(), vec![Hook::Died(0, 1)]); - }); -} - -#[test] -fn imbalances_should_work() { - use frame_support::traits::tokens::fungibles::Balanced; + Balances::make_free_balance_be(&1, 30); - new_test_ext().execute_with(|| { + // Cannot add metadata to unknown asset assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_noop!( + Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 10], false), + Error::::Unknown, + ); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); + // Cannot add metadata to unowned asset + assert_noop!( + Uniques::set_metadata(Origin::signed(2), 0, 42, vec![0u8; 10], vec![0u8; 10], false), + Error::::NoPermission, + ); - let imb = Uniques::issue(0, 100); - assert_eq!(Uniques::total_supply(0), 100); - 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!(Uniques::total_supply(0), 30); + // Cannot add oversized metadata + assert_noop!( + Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 51], vec![0u8; 1], false), + Error::::BadMetadata, + ); + assert_noop!( + Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 1], vec![0u8; 51], false), + Error::::BadMetadata, + ); - assert!(Uniques::resolve(&1, imb1).is_ok()); - assert_eq!(Uniques::balance(0, 1), 30); - assert_eq!(Uniques::total_supply(0), 30); - }); -} + // Successfully add metadata and take deposit + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 10], false)); + assert_eq!(Balances::free_balance(&1), 9); + assert!(InstanceMetadataOf::::contains_key(0, 42)); -#[test] -fn force_metadata_should_work() { - new_test_ext().execute_with(|| { - //force set metadata works - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::force_set_metadata(Origin::root(), 0, vec![0u8; 10], vec![0u8; 10], 8, false)); - assert!(Metadata::::contains_key(0)); + // Force origin works, too. + assert_ok!(Uniques::set_metadata(Origin::root(), 0, 42, vec![0u8; 9], vec![0u8; 9], false)); - //overwrites existing metadata - let asset_original_metadata = Metadata::::get(0); - assert_ok!(Uniques::force_set_metadata(Origin::root(), 0, vec![1u8; 10], vec![1u8; 10], 8, false)); - assert_ne!(Metadata::::get(0), asset_original_metadata); + // Update deposit + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 5], false)); + assert_eq!(Balances::free_balance(&1), 14); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 15], false)); + assert_eq!(Balances::free_balance(&1), 4); - //attempt to set metadata for non-existent asset class + // Cannot over-reserve assert_noop!( - Uniques::force_set_metadata(Origin::root(), 1, vec![0u8; 10], vec![0u8; 10], 8, false), - Error::::Unknown + Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 20], vec![0u8; 20], false), + BalancesError::::InsufficientBalance, ); - //string length limit check - let limit = StringLimit::get() as usize; + // Can't set or clear metadata once frozen + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 5], true)); assert_noop!( - Uniques::force_set_metadata(Origin::root(), 0, vec![0u8; limit + 1], vec![0u8; 10], 8, false), - Error::::BadMetadata - ); - assert_noop!( - Uniques::force_set_metadata(Origin::root(), 0, vec![0u8; 10], vec![0u8; limit + 1], 8, false), - Error::::BadMetadata + Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 5], false), + Error::::Frozen, ); + assert_noop!(Uniques::clear_metadata(Origin::signed(1), 0, 42), Error::::Frozen); - //force clear metadata works - assert!(Metadata::::contains_key(0)); - assert_ok!(Uniques::force_clear_metadata(Origin::root(), 0)); - assert!(!Metadata::::contains_key(0)); - - //Error handles clearing non-existent asset class - assert_noop!(Uniques::force_clear_metadata(Origin::root(), 1), Error::::Unknown); + // Clear Metadata + assert_ok!(Uniques::set_metadata(Origin::root(), 0, 42, vec![0u8; 10], vec![0u8; 5], false)); + assert_noop!(Uniques::clear_metadata(Origin::signed(2), 0, 42), Error::::NoPermission); + assert_noop!(Uniques::clear_metadata(Origin::signed(1), 1, 42), Error::::Unknown); + assert_ok!(Uniques::clear_metadata(Origin::signed(1), 0, 42)); + assert!(!InstanceMetadataOf::::contains_key(0, 42)); }); } +// TODO: burn + +/* #[test] fn force_asset_status_should_work(){ new_test_ext().execute_with(|| { @@ -523,30 +429,6 @@ fn approval_deposits_work() { }); } -#[test] -fn cannot_transfer_more_than_approved() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - Balances::make_free_balance_be(&1, 1); - assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); - let e = Error::::Unapproved; - assert_noop!(Uniques::transfer_approved(Origin::signed(2), 0, 1, 3, 51), e); - }); -} - -#[test] -fn cannot_transfer_more_than_exists() { - new_test_ext().execute_with(|| { - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - Balances::make_free_balance_be(&1, 1); - assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 101)); - let e = Error::::BalanceLow; - assert_noop!(Uniques::transfer_approved(Origin::signed(2), 0, 1, 3, 101), e); - }); -} - #[test] fn cancel_approval_works() { new_test_ext().execute_with(|| { diff --git a/frame/uniques/src/types.rs b/frame/uniques/src/types.rs index a9e4bc8d36f68..fa32c83c7f912 100644 --- a/frame/uniques/src/types.rs +++ b/frame/uniques/src/types.rs @@ -46,6 +46,7 @@ pub struct ClassDetails< pub(super) free_holds: u32, /// Whether the asset is frozen for non-admin transfers. pub(super) is_frozen: bool, + } /// Witness data for the destroy transactions. From d4102980faf073a3c9e5872f47fcd9c04c29e1f7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 19 May 2021 15:36:18 +0200 Subject: [PATCH 25/54] refresh_deposit --- frame/uniques/src/lib.rs | 113 +++++++++++++++++++++++++---------- frame/uniques/src/tests.rs | 25 +++++--- frame/uniques/src/weights.rs | 17 +----- 3 files changed, 100 insertions(+), 55 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 2a3b7f1ca0d96..7545259c13ecb 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -420,14 +420,15 @@ pub mod pallet { .ok_or(ArithmeticError::Overflow)?; class_details.instances = instances; - let deposit = if class_details.free_holding { + let deposit = match class_details.free_holding { + true => Zero::zero(), + false => T::InstanceDeposit::get(), + }; + if deposit.is_zero() { class_details.free_holds += 1; - Zero::zero() } else { - let deposit = T::InstanceDeposit::get(); T::Currency::reserve(&class_details.owner, deposit)?; class_details.total_deposit += deposit; - deposit }; let owner = owner.clone(); @@ -542,6 +543,56 @@ pub mod pallet { Ok(()) } + /// Disallow further unprivileged transfer of an asset instance. + /// + /// Origin must be Signed and the sender should be the Freezer of the asset `class`. + /// + /// - `class`: The class of the asset to be frozen. + /// - `instance`: The instance of the asset to be frozen. + /// + /// Emits `Frozen`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::refresh_deposit())] + pub(super) fn refresh_deposit( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + #[pallet::compact] instance: T::InstanceId, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + + let mut details = Asset::::get(&class, &instance).ok_or(Error::::Unknown)?; + let mut class_details = Class::::get(&class).ok_or(Error::::Unknown)?; + ensure!(class_details.owner == origin, Error::::NoPermission); + + let deposit = match class_details.free_holding { + true => Zero::zero(), + false => T::InstanceDeposit::get(), + }; + if deposit.is_zero() { + class_details.free_holds += 1; + } else { + T::Currency::reserve(&class_details.owner, deposit)?; + class_details.total_deposit += deposit; + }; + if details.deposit > deposit { + T::Currency::unreserve(&class_details.owner, details.deposit - deposit); + } else { + T::Currency::reserve(&class_details.owner, deposit - details.deposit)?; + } + class_details.total_deposit = class_details.total_deposit + .saturated_sub(details.deposit) + .saturated_add(deposit); + if details.deposit.is_zero() { + + } + details.deposit = deposit; + + Asset::::insert(&class, &instance, &details); + Class::::insert(&class, &class_details); + Ok(()) + } + /// Disallow further unprivileged transfer of an asset instance. /// /// Origin must be Signed and the sender should be the Freezer of the asset `class`. @@ -562,7 +613,6 @@ pub mod pallet { let mut details = Asset::::get(&class, &instance) .ok_or(Error::::Unknown)?; - let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; ensure!(class_details.freezer == origin, Error::::NoPermission); details.is_frozen = true; @@ -943,21 +993,20 @@ pub mod pallet { let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); class_details.total_deposit = class_details.total_deposit.saturating_sub(old_deposit); - let deposit = if let Some(owner) = maybe_check_owner { - let deposit = T::MetadataDepositPerByte::get() - .saturating_mul(((name.len() + info.len()) as u32).into()) - .saturating_add(T::MetadataDepositBase::get()); - - if deposit > old_deposit { - T::Currency::reserve(&owner, deposit - old_deposit)?; - } else { - T::Currency::unreserve(&owner, old_deposit - deposit); + let mut deposit = old_deposit; + if !class_details.free_holding { + if let Some(owner) = maybe_check_owner { + deposit = T::MetadataDepositPerByte::get() + .saturating_mul(((name.len() + info.len()) as u32).into()) + .saturating_add(T::MetadataDepositBase::get()); + + if deposit > old_deposit { + T::Currency::reserve(&owner, deposit - old_deposit)?; + } else { + T::Currency::unreserve(&owner, old_deposit - deposit); + } } - - deposit - } else { - old_deposit - }; + } class_details.total_deposit = class_details.total_deposit.saturating_add(deposit); *metadata = Some(InstanceMetadata { @@ -1058,20 +1107,20 @@ pub mod pallet { let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); details.total_deposit = details.total_deposit.saturating_sub(old_deposit); - let deposit = if let Some(owner) = maybe_check_owner { - let deposit = T::MetadataDepositPerByte::get() - .saturating_mul(((name.len() + info.len()) as u32).into()) - .saturating_add(T::MetadataDepositBase::get()); - - if deposit > old_deposit { - T::Currency::reserve(&owner, deposit - old_deposit)?; - } else { - T::Currency::unreserve(&owner, old_deposit - deposit); + let mut deposit = old_deposit; + if let Some(owner) = maybe_check_owner { + if !details.free_holding { + deposit = T::MetadataDepositPerByte::get() + .saturating_mul(((name.len() + info.len()) as u32).into()) + .saturating_add(T::MetadataDepositBase::get()); + + if deposit > old_deposit { + T::Currency::reserve(&owner, deposit - old_deposit)?; + } else { + T::Currency::unreserve(&owner, old_deposit - deposit); + } } - deposit - } else { - old_deposit - }; + } details.total_deposit = details.total_deposit.saturating_add(deposit); Class::::insert(&class, details); diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index 9b0d8b6f8cdd3..342ae6f3af26c 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -350,21 +350,24 @@ fn set_instance_metadata_should_work() { }); } -// TODO: burn - -/* #[test] fn force_asset_status_should_work(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 10); Balances::make_free_balance_be(&2, 10); - assert_ok!(Uniques::create(Origin::signed(1), 0, 1, 30)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 1, 50)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 2, 150)); + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 69, 2)); + assert_ok!(Uniques::set_class_metadata) + assert_eq!(Uniques::reserved_balance(0), 96); + + //force asset status to be free holding + assert_ok!(Uniques::force_asset_status(Origin::root(), 0, 1, 1, 1, 1, true, false)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 142, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 169, 2)); + assert_ok!(Uniques::set_class_metadata) + assert_eq!(Uniques::reserved_balance(0), 96); - //force asset status to change min_balance > balance - assert_ok!(Uniques::force_asset_status(Origin::root(), 0, 1, 1, 1, 1, 100, true, false)); - assert_eq!(Uniques::balance(0, 1), 50); //account can recieve assets for balance < min_balance assert_ok!(Uniques::transfer(Origin::signed(2), 0, 1, 1)); @@ -392,6 +395,10 @@ fn force_asset_status_should_work(){ }); } +// TODO: burn + +/* + #[test] fn approval_lifecycle_works() { new_test_ext().execute_with(|| { diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index b62bacd1e217c..fbf5c0b90591d 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -50,7 +50,7 @@ pub trait WeightInfo { fn mint() -> Weight; fn burn() -> Weight; fn transfer() -> Weight; - fn force_transfer() -> Weight; + fn refresh_deposit() -> Weight; fn freeze() -> Weight; fn thaw() -> Weight; fn freeze_class() -> Weight; @@ -58,7 +58,6 @@ pub trait WeightInfo { fn transfer_ownership() -> Weight; fn set_team() -> Weight; fn approve_transfer() -> Weight; - fn transfer_approved() -> Weight; fn cancel_approval() -> Weight; fn force_cancel_approval() -> Weight; fn force_asset_status() -> Weight; @@ -109,7 +108,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn force_transfer() -> Weight { + fn refresh_deposit() -> Weight { (70_968_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) @@ -149,11 +148,6 @@ impl WeightInfo for SubstrateWeight { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn transfer_approved() -> Weight { - (90_338_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(5 as Weight)) - } fn cancel_approval() -> Weight { (48_591_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) @@ -237,7 +231,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } - fn force_transfer() -> Weight { + fn refresh_deposit() -> Weight { (70_968_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) @@ -277,11 +271,6 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn transfer_approved() -> Weight { - (90_338_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(5 as Weight)) - } fn cancel_approval() -> Weight { (48_591_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) From d7035103fff775ef5016d81b8cf0f996ac200406 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 19 May 2021 17:46:38 +0200 Subject: [PATCH 26/54] Tests and proper handling of metdata destruction --- frame/uniques/src/lib.rs | 165 ++++++++++++++-------------- frame/uniques/src/tests.rs | 66 +++++------ frame/uniques/src/types.rs | 15 ++- frame/uniques/src/weights.rs | 6 +- primitives/arithmetic/src/traits.rs | 28 +++++ 5 files changed, 149 insertions(+), 131 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 7545259c13ecb..faeddc79b6ef6 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -286,7 +286,7 @@ pub mod pallet { total_deposit: deposit, free_holding: false, instances: 0, - free_holds: 0, + instance_metadatas: 0, is_frozen: false, }, ); @@ -332,7 +332,7 @@ pub mod pallet { total_deposit: Zero::zero(), free_holding, instances: 0, - free_holds: 0, + instance_metadatas: 0, is_frozen: false, }, ); @@ -352,11 +352,11 @@ pub mod pallet { /// Emits `Destroyed` event when successful. /// /// Weight: `O(i + f)` where: - /// - `i = (witness.instances - witness.free_holds)` - /// - `f = witness.free_holds` + /// - `i = witness.instances` + /// - `f = witness.instance_metdadatas` #[pallet::weight(T::WeightInfo::destroy( - witness.instances.saturating_sub(witness.free_holds), - witness.free_holds, + witness.instances, + witness.instance_metadatas, ))] pub(super) fn destroy( origin: OriginFor, @@ -373,12 +373,12 @@ pub mod pallet { ensure!(class_details.owner == check_owner, Error::::NoPermission); } ensure!(class_details.instances == witness.instances, Error::::BadWitness); - ensure!(class_details.free_holds == witness.free_holds, Error::::BadWitness); + ensure!(class_details.instance_metadatas == witness.instance_metadatas, Error::::BadWitness); for (instance, details) in Asset::::drain_prefix(&class) { Account::::remove((&details.owner, &class, &instance)); - InstanceMetadataOf::::remove(&class, &instance); } + InstanceMetadataOf::::remove_prefix(&class); ClassMetadataOf::::remove(&class); T::Currency::unreserve(&class_details.owner, class_details.total_deposit); @@ -424,12 +424,8 @@ pub mod pallet { true => Zero::zero(), false => T::InstanceDeposit::get(), }; - if deposit.is_zero() { - class_details.free_holds += 1; - } else { - T::Currency::reserve(&class_details.owner, deposit)?; - class_details.total_deposit += deposit; - }; + T::Currency::reserve(&class_details.owner, deposit)?; + class_details.total_deposit += deposit; let owner = owner.clone(); Account::::insert((&owner, &class, &instance), ()); @@ -473,19 +469,16 @@ pub mod pallet { ensure!(is_permitted, Error::::NoPermission); ensure!(check_owner.map_or(true, |o| o == details.owner), Error::::WrongOwner); - if !details.deposit.is_zero() { - // Return the deposit. - T::Currency::unreserve(&class_details.owner, details.deposit); - class_details.total_deposit = class_details.total_deposit - .saturating_sub(details.deposit); - } - class_details.instances = class_details.instances.saturating_sub(1); + // Return the deposit. + T::Currency::unreserve(&class_details.owner, details.deposit); + class_details.total_deposit.saturating_reduce(details.deposit); + class_details.instances.saturating_dec(); if let Some(metadata) = InstanceMetadataOf::::take(&class, &instance) { // Remove instance metadata - class_details.total_deposit = class_details.total_deposit - .saturating_sub(metadata.deposit); T::Currency::unreserve(&class_details.owner, metadata.deposit); + class_details.total_deposit.saturating_reduce(metadata.deposit); + class_details.instance_metadatas.saturating_dec(); } Ok(details.owner) })?; @@ -543,53 +536,60 @@ pub mod pallet { Ok(()) } - /// Disallow further unprivileged transfer of an asset instance. + /// Reevaluate the deposits on some assets. /// - /// Origin must be Signed and the sender should be the Freezer of the asset `class`. + /// Origin must be Signed and the sender should be the Owner of the asset `class`. /// /// - `class`: The class of the asset to be frozen. - /// - `instance`: The instance of the asset to be frozen. + /// - `instances`: The instances of the asset class whose deposits will be reevaluated. /// - /// Emits `Frozen`. + /// NOTE: This exists as a best-effort function. Any asset instances which are unknown or + /// in the case that the owner account does not have reservable funds to pay for a + /// deposit increase are ignored. Generally the owner isn't going to call this on instances + /// whose existing deposit is less than the refreshed deposit as it would only cost them, + /// so it's of little consequence. /// - /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::refresh_deposit())] - pub(super) fn refresh_deposit( + /// It will still return an error in the case that the class is unknown of the signer is + /// not permitted to call it. + /// + /// Weight: `O(instances.len())` + #[pallet::weight(T::WeightInfo::redeposit(instances.len() as u32))] + pub(super) fn redeposit( origin: OriginFor, #[pallet::compact] class: T::ClassId, - #[pallet::compact] instance: T::InstanceId, + instances: Vec, ) -> DispatchResult { let origin = ensure_signed(origin)?; - let mut details = Asset::::get(&class, &instance).ok_or(Error::::Unknown)?; let mut class_details = Class::::get(&class).ok_or(Error::::Unknown)?; ensure!(class_details.owner == origin, Error::::NoPermission); - let deposit = match class_details.free_holding { true => Zero::zero(), false => T::InstanceDeposit::get(), }; - if deposit.is_zero() { - class_details.free_holds += 1; - } else { - T::Currency::reserve(&class_details.owner, deposit)?; - class_details.total_deposit += deposit; - }; - if details.deposit > deposit { - T::Currency::unreserve(&class_details.owner, details.deposit - deposit); - } else { - T::Currency::reserve(&class_details.owner, deposit - details.deposit)?; - } - class_details.total_deposit = class_details.total_deposit - .saturated_sub(details.deposit) - .saturated_add(deposit); - if details.deposit.is_zero() { + for instance in instances.into_iter() { + let mut details = match Asset::::get(&class, &instance) { + Some(x) => x, + None => continue, + }; + let old = details.deposit; + if old > deposit { + T::Currency::unreserve(&class_details.owner, old - deposit); + } else if deposit > old { + if T::Currency::reserve(&class_details.owner, deposit - old).is_err() { + // NOTE: No alterations made to class_details in this iteration so far, so + // this is OK to do. + continue + } + } + class_details.total_deposit.saturating_accrue(deposit); + class_details.total_deposit.saturating_reduce(old); + details.deposit = deposit; + Asset::::insert(&class, &instance, &details); } - details.deposit = deposit; - - Asset::::insert(&class, &instance, &details); Class::::insert(&class, &class_details); + Ok(()) } @@ -613,6 +613,7 @@ pub mod pallet { let mut details = Asset::::get(&class, &instance) .ok_or(Error::::Unknown)?; + let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; ensure!(class_details.freezer == origin, Error::::NoPermission); details.is_frozen = true; @@ -991,23 +992,23 @@ pub mod pallet { let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); + if metadata.is_none() { + class_details.instance_metadatas.saturating_inc(); + } let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); - class_details.total_deposit = class_details.total_deposit.saturating_sub(old_deposit); - let mut deposit = old_deposit; - if !class_details.free_holding { - if let Some(owner) = maybe_check_owner { - deposit = T::MetadataDepositPerByte::get() - .saturating_mul(((name.len() + info.len()) as u32).into()) - .saturating_add(T::MetadataDepositBase::get()); - - if deposit > old_deposit { - T::Currency::reserve(&owner, deposit - old_deposit)?; - } else { - T::Currency::unreserve(&owner, old_deposit - deposit); - } - } + class_details.total_deposit.saturating_reduce(old_deposit); + let mut deposit = Zero::zero(); + if !class_details.free_holding && maybe_check_owner.is_some() { + deposit = T::MetadataDepositPerByte::get() + .saturating_mul(((name.len() + info.len()) as u32).into()) + .saturating_add(T::MetadataDepositBase::get()); + } + if deposit > old_deposit { + T::Currency::reserve(&class_details.owner, deposit - old_deposit)?; + } else if deposit < old_deposit { + T::Currency::unreserve(&class_details.owner, old_deposit - deposit); } - class_details.total_deposit = class_details.total_deposit.saturating_add(deposit); + class_details.total_deposit.saturating_accrue(deposit); *metadata = Some(InstanceMetadata { deposit, @@ -1055,9 +1056,12 @@ pub mod pallet { let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); + if metadata.is_some() { + class_details.instance_metadatas.saturating_dec(); + } let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; T::Currency::unreserve(&class_details.owner, deposit); - class_details.total_deposit = class_details.total_deposit.saturating_sub(deposit); + class_details.total_deposit.saturating_reduce(deposit); Class::::insert(&class, &class_details); Self::deposit_event(Event::MetadataCleared(class)); @@ -1106,22 +1110,19 @@ pub mod pallet { ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); - details.total_deposit = details.total_deposit.saturating_sub(old_deposit); - let mut deposit = old_deposit; - if let Some(owner) = maybe_check_owner { - if !details.free_holding { - deposit = T::MetadataDepositPerByte::get() - .saturating_mul(((name.len() + info.len()) as u32).into()) - .saturating_add(T::MetadataDepositBase::get()); - - if deposit > old_deposit { - T::Currency::reserve(&owner, deposit - old_deposit)?; - } else { - T::Currency::unreserve(&owner, old_deposit - deposit); - } - } + details.total_deposit.saturating_reduce(old_deposit); + let mut deposit = Zero::zero(); + if maybe_check_owner.is_some() && !details.free_holding { + deposit = T::MetadataDepositPerByte::get() + .saturating_mul(((name.len() + info.len()) as u32).into()) + .saturating_add(T::MetadataDepositBase::get()); + } + if deposit > old_deposit { + T::Currency::reserve(&details.owner, deposit - old_deposit)?; + } else if deposit < old_deposit { + T::Currency::unreserve(&details.owner, old_deposit - deposit); } - details.total_deposit = details.total_deposit.saturating_add(deposit); + details.total_deposit.saturating_accrue(deposit); Class::::insert(&class, details); diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index 342ae6f3af26c..eaac2a83e8df0 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -45,10 +45,6 @@ fn assets() -> Vec<(u64, u32, u32)> { let details = Class::::get(class).unwrap(); let instances = Asset::::iter_prefix(class).count() as u32; assert_eq!(details.instances, instances); - let free_holds = Asset::::iter_prefix(class) - .filter(|(_, item)| item.deposit.is_zero()) - .count() as u32; - assert_eq!(details.free_holds, free_holds); } r } @@ -89,6 +85,8 @@ fn lifecycle_should_work() { assert_ok!(Uniques::mint(Origin::signed(1), 0, 69, 20)); assert_eq!(Balances::reserved_balance(&1), 7); assert_eq!(assets(), vec![(10, 0, 42), (20, 0, 69)]); + assert_eq!(Class::::get(0).unwrap().instances, 2); + assert_eq!(Class::::get(0).unwrap().instance_metadatas, 0); assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![42], vec![42], false)); assert_eq!(Balances::reserved_balance(&1), 10); @@ -98,6 +96,8 @@ fn lifecycle_should_work() { assert!(InstanceMetadataOf::::contains_key(0, 69)); let w = Class::::get(0).unwrap().destroy_witness(); + assert_eq!(w.instances, 2); + assert_eq!(w.instance_metadatas, 2); assert_ok!(Uniques::destroy(Origin::signed(1), 0, w)); assert_eq!(Balances::reserved_balance(&1), 0); @@ -230,7 +230,7 @@ fn set_class_metadata_should_work() { Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], false), Error::::Unknown, ); - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); // Cannot add metadata to unowned asset assert_noop!( Uniques::set_class_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], false), @@ -291,7 +291,7 @@ fn set_instance_metadata_should_work() { Balances::make_free_balance_be(&1, 30); // Cannot add metadata to unknown asset - assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); assert_noop!( Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 10], false), Error::::Unknown, @@ -315,7 +315,7 @@ fn set_instance_metadata_should_work() { // Successfully add metadata and take deposit assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 10], false)); - assert_eq!(Balances::free_balance(&1), 9); + assert_eq!(Balances::free_balance(&1), 8); assert!(InstanceMetadataOf::::contains_key(0, 42)); // Force origin works, too. @@ -323,9 +323,9 @@ fn set_instance_metadata_should_work() { // Update deposit assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 5], false)); - assert_eq!(Balances::free_balance(&1), 14); + assert_eq!(Balances::free_balance(&1), 13); assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 15], false)); - assert_eq!(Balances::free_balance(&1), 4); + assert_eq!(Balances::free_balance(&1), 3); // Cannot over-reserve assert_noop!( @@ -353,52 +353,42 @@ fn set_instance_metadata_should_work() { #[test] fn force_asset_status_should_work(){ new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 10); - Balances::make_free_balance_be(&2, 10); - assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + Balances::make_free_balance_be(&1, 100); + + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 69, 2)); - assert_ok!(Uniques::set_class_metadata) - assert_eq!(Uniques::reserved_balance(0), 96); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0; 10], vec![0; 10], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0; 10], vec![0; 10], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, vec![0; 10], vec![0; 10], false)); + assert_eq!(Balances::reserved_balance(1), 65); //force asset status to be free holding assert_ok!(Uniques::force_asset_status(Origin::root(), 0, 1, 1, 1, 1, true, false)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 142, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 169, 2)); - assert_ok!(Uniques::set_class_metadata) - assert_eq!(Uniques::reserved_balance(0), 96); - - - //account can recieve assets for balance < min_balance - assert_ok!(Uniques::transfer(Origin::signed(2), 0, 1, 1)); - assert_eq!(Uniques::balance(0, 1), 51); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 142, vec![0; 10], vec![0; 10], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 169, vec![0; 10], vec![0; 10], false)); + assert_eq!(Balances::reserved_balance(1), 65); - //account on outbound transfer will cleanup for balance < min_balance - assert_ok!(Uniques::transfer(Origin::signed(1), 0, 2, 1)); - assert_eq!(Uniques::balance(0,1), 0); + assert_ok!(Uniques::redeposit(Origin::signed(1), 0, vec![0, 42, 50, 69, 100])); + assert_eq!(Balances::reserved_balance(1), 63); - //won't create new account with balance below min_balance - assert_noop!(Uniques::transfer(Origin::signed(2), 0, 3, 50), TokenError::BelowMinimum); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0; 10], vec![0; 10], false)); + assert_eq!(Balances::reserved_balance(1), 42); - //force asset status will not execute for non-existent class - assert_noop!( - Uniques::force_asset_status(Origin::root(), 1, 1, 1, 1, 1, 90, true, false), - Error::::Unknown - ); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, vec![0; 10], vec![0; 10], false)); + assert_eq!(Balances::reserved_balance(1), 21); - //account drains to completion when funds dip below min_balance - assert_ok!(Uniques::force_asset_status(Origin::root(), 0, 1, 1, 1, 1, 110, true, false)); - assert_ok!(Uniques::transfer(Origin::signed(2), 0, 1, 110)); - assert_eq!(Uniques::balance(0, 1), 200); - assert_eq!(Uniques::balance(0, 2), 0); - assert_eq!(Uniques::total_supply(0), 200); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0; 10], vec![0; 10], false)); + assert_eq!(Balances::reserved_balance(1), 0); }); } // TODO: burn +// TODO: approvals /* - #[test] fn approval_lifecycle_works() { new_test_ext().execute_with(|| { diff --git a/frame/uniques/src/types.rs b/frame/uniques/src/types.rs index fa32c83c7f912..fb742ae7ade2c 100644 --- a/frame/uniques/src/types.rs +++ b/frame/uniques/src/types.rs @@ -40,31 +40,30 @@ pub struct ClassDetails< pub(super) total_deposit: DepositBalance, /// If `true`, then no deposit is needed to hold instances of this class. pub(super) free_holding: bool, - /// The total number of outstanding unique instances of this asset class. + /// The total number of outstanding instances of this asset class. pub(super) instances: u32, - /// The number of instances of this asset class with a zero deposit. - pub(super) free_holds: u32, + /// The total number of outstanding instance metadata of this asset class. + pub(super) instance_metadatas: u32, /// Whether the asset is frozen for non-admin transfers. pub(super) is_frozen: bool, - } /// Witness data for the destroy transactions. #[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)] pub struct DestroyWitness { - /// The number of instances of this class of asset. + /// The total number of outstanding instances of this asset class. #[codec(compact)] pub(super) instances: u32, - /// The number of instances of this asset class with a zero deposit. + /// The total number of outstanding instance metadata of this asset class. #[codec(compact)] - pub(super) free_holds: u32, + pub(super) instance_metadatas: u32, } impl ClassDetails { pub fn destroy_witness(&self) -> DestroyWitness { DestroyWitness { instances: self.instances, - free_holds: self.free_holds, + instance_metadatas: self.instance_metadatas, } } } diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index fbf5c0b90591d..ca4ddc6377038 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -50,7 +50,7 @@ pub trait WeightInfo { fn mint() -> Weight; fn burn() -> Weight; fn transfer() -> Weight; - fn refresh_deposit() -> Weight; + fn redeposit(i: u32, ) -> Weight; fn freeze() -> Weight; fn thaw() -> Weight; fn freeze_class() -> Weight; @@ -108,7 +108,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn refresh_deposit() -> Weight { + fn redeposit(_i: u32, ) -> Weight { (70_968_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) @@ -231,7 +231,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } - fn refresh_deposit() -> Weight { + fn redeposit(_i: u32, ) -> Weight { (70_968_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index ea297077e351c..d0ce921d9d342 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -127,6 +127,34 @@ pub trait Saturating { /// Saturating exponentiation. Compute `self.pow(exp)`, saturating at the numeric bounds /// instead of overflowing. fn saturating_pow(self, exp: usize) -> Self; + + /// Increment self by one, saturating. + fn saturating_inc(&mut self) where Self: One { + let mut o = Self::one(); + sp_std::mem::swap(&mut o, self); + *self = o.saturating_add(One::one()); + } + + /// Decrement self by one, saturating at zero. + fn saturating_dec(&mut self) where Self: One { + let mut o = Self::one(); + sp_std::mem::swap(&mut o, self); + *self = o.saturating_sub(One::one()); + } + + /// Increment self by some `amount`, saturating. + fn saturating_accrue(&mut self, amount: Self) where Self: One { + let mut o = Self::one(); + sp_std::mem::swap(&mut o, self); + *self = o.saturating_add(amount); + } + + /// Decrement self by some `amount`, saturating at zero. + fn saturating_reduce(&mut self, amount: Self) where Self: One { + let mut o = Self::one(); + sp_std::mem::swap(&mut o, self); + *self = o.saturating_sub(amount); + } } impl Saturating for T { From 59ff5e9bce7cb4d5e20a37e3ab7839c08915e9a9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 19 May 2021 19:10:18 +0200 Subject: [PATCH 27/54] test burn --- frame/uniques/src/tests.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index eaac2a83e8df0..372cd42178cb8 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -385,7 +385,29 @@ fn force_asset_status_should_work(){ }); } -// TODO: burn +#[test] +fn burn_works() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); + assert_ok!(Uniques::set_team(Origin::signed(1), 0, 2, 3, 4)); + + assert_noop!(Uniques::burn(Origin::signed(5), 0, 42, Some(5)), Error::::Unknown); + + assert_ok!(Uniques::mint(Origin::signed(2), 0, 42, 5)); + assert_ok!(Uniques::mint(Origin::signed(2), 0, 69, 5)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, vec![0; 1], vec![0; 1], false)); + assert_eq!(Balances::reserved_balance(1), 5); + + assert_noop!(Uniques::burn(Origin::signed(0), 0, 42, None), Error::::NoPermission); + assert_noop!(Uniques::burn(Origin::signed(5), 0, 42, Some(6)), Error::::WrongOwner); + + assert_ok!(Uniques::burn(Origin::signed(5), 0, 42, Some(5))); + assert_ok!(Uniques::burn(Origin::signed(3), 0, 69, Some(5))); + assert_eq!(Balances::reserved_balance(1), 0); + }); +} + // TODO: approvals /* From 4be89284748fba3f7cbc261d0e2ce5733c9db653 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 19 May 2021 20:33:58 +0200 Subject: [PATCH 28/54] Tests --- frame/uniques/src/lib.rs | 73 ++++++++++------------------- frame/uniques/src/tests.rs | 89 +++++++++++++++--------------------- frame/uniques/src/weights.rs | 11 ----- 3 files changed, 61 insertions(+), 112 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index faeddc79b6ef6..a93318a4459f1 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -803,27 +803,38 @@ pub mod pallet { #[pallet::compact] instance: T::InstanceId, delegate: ::Source, ) -> DispatchResult { - let origin = ensure_signed(origin)?; + let maybe_check: Option = T::ForceOrigin::try_origin(origin) + .map(|_| None) + .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; + let delegate = T::Lookup::lookup(delegate)?; + let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; let mut details = Asset::::get(&class, &instance) .ok_or(Error::::Unknown)?; - ensure!(details.owner == origin, Error::::NoPermission); + + if let Some(check) = maybe_check { + let permitted = &check == &class_details.admin || &check == &details.owner; + ensure!(permitted, Error::::NoPermission); + } details.approved = Some(delegate); Asset::::insert(&class, &instance, &details); let delegate = details.approved.expect("set as Some above; qed"); - Self::deposit_event(Event::ApprovedTransfer(class, instance, origin, delegate)); + Self::deposit_event(Event::ApprovedTransfer(class, instance, details.owner, delegate)); Ok(()) } /// Cancel the prior approval for the transfer of an asset by a delegate. /// - /// Origin must be Signed and there must be an approval in place between signer and - /// `delegate`. + /// Origin must be either: + /// - the `Force` origin; + /// - `Signed` with the signer being the Admin of the asset `class`; + /// - `Signed` with the signer being the Owner of the asset `instance`; /// + /// Arguments: /// - `class`: The class of the asset of whose approval will be cancelled. /// - `instance`: The instance of the asset of whose approval will be cancelled. /// - `maybe_check_delegate`: If `Some` will ensure that the given account is the one to @@ -839,56 +850,18 @@ pub mod pallet { #[pallet::compact] instance: T::InstanceId, maybe_check_delegate: Option<::Source>, ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let maybe_check_delegate = maybe_check_delegate.map(T::Lookup::lookup).transpose()?; + let maybe_check: Option = T::ForceOrigin::try_origin(origin) + .map(|_| None) + .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; + let class_details = Class::::get(&class).ok_or(Error::::Unknown)?; let mut details = Asset::::get(&class, &instance) .ok_or(Error::::Unknown)?; - ensure!(details.owner == origin, Error::::NoPermission); - let old = details.approved.take().ok_or(Error::::NoDelegate)?; - if let Some(check_delegate) = maybe_check_delegate { - ensure!(check_delegate == old, Error::::WrongDelegate); + if let Some(check) = maybe_check { + let permitted = &check == &class_details.admin || &check == &details.owner; + ensure!(permitted, Error::::NoPermission); } - - Asset::::insert(&class, &instance, &details); - Self::deposit_event(Event::ApprovalCancelled(class, instance, origin, old)); - - Ok(()) - } - - /// Cancel the prior approval for the transfer of an asset by a delegate. - /// - /// Origin must be either ForceOrigin or Signed origin with the signer being the Admin - /// account of the asset `class`. - /// - /// - `class`: The class of the asset of whose approval will be cancelled. - /// - `instance`: The instance of the asset of whose approval will be cancelled. - /// - `maybe_check_delegate`: If `Some` will ensure that the given account is the one to - /// which permission of transfer is delegated. - /// - /// Emits `ApprovalCancelled` on success. - /// - /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::force_cancel_approval())] - pub(super) fn force_cancel_approval( - origin: OriginFor, - #[pallet::compact] class: T::ClassId, - #[pallet::compact] instance: T::InstanceId, - maybe_check_delegate: Option<::Source>, - ) -> DispatchResult { - T::ForceOrigin::try_origin(origin) - .map(|_| ()) - .or_else(|origin| -> DispatchResult { - let origin = ensure_signed(origin)?; - let d = Class::::get(&class).ok_or(Error::::Unknown)?; - ensure!(&origin == &d.admin, Error::::NoPermission); - Ok(()) - })?; - let maybe_check_delegate = maybe_check_delegate.map(T::Lookup::lookup).transpose()?; - - let mut details = Asset::::get(&class, &instance) - .ok_or(Error::::Unknown)?; let old = details.approved.take().ok_or(Error::::NoDelegate)?; if let Some(check_delegate) = maybe_check_delegate { ensure!(check_delegate == old, Error::::WrongDelegate); diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index 372cd42178cb8..7f52acbe1e054 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -21,11 +21,7 @@ use super::*; use crate::mock::*; use frame_support::{assert_ok, assert_noop, traits::Currency}; use pallet_balances::Error as BalancesError; -/* -fn last_event() -> mock::Event { - frame_system::Pallet::::events().pop().expect("Event expected").event -} -*/ + fn assets() -> Vec<(u64, u32, u32)> { let mut r: Vec<_> = Account::::iter().map(|x| x.0).collect(); r.sort(); @@ -408,75 +404,66 @@ fn burn_works() { }); } -// TODO: approvals - -/* #[test] fn approval_lifecycle_works() { new_test_ext().execute_with(|| { assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - Balances::make_free_balance_be(&1, 1); - assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Balances::reserved_balance(&1), 1); - assert_ok!(Uniques::transfer_approved(Origin::signed(2), 0, 1, 3, 40)); - assert_ok!(Uniques::cancel_approval(Origin::signed(1), 0, 2)); - assert_eq!(Uniques::balance(0, 1), 60); - assert_eq!(Uniques::balance(0, 3), 40); - assert_eq!(Balances::reserved_balance(&1), 0); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 2)); + assert_ok!(Uniques::approve_transfer(Origin::signed(2), 0, 42, 3)); + assert_ok!(Uniques::transfer(Origin::signed(3), 0, 42, 4)); + assert_noop!(Uniques::transfer(Origin::signed(3), 0, 42, 3), Error::::NoPermission); + assert!(Asset::::get(0, 42).unwrap().approved.is_none()); + + assert_ok!(Uniques::approve_transfer(Origin::signed(4), 0, 42, 2)); + assert_ok!(Uniques::transfer(Origin::signed(2), 0, 42, 2)); }); } #[test] -fn approval_deposits_work() { +fn cancel_approval_works() { new_test_ext().execute_with(|| { assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - let e = BalancesError::::InsufficientBalance; - assert_noop!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50), e); - - Balances::make_free_balance_be(&1, 1); - assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); - assert_eq!(Balances::reserved_balance(&1), 1); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 2)); - assert_ok!(Uniques::transfer_approved(Origin::signed(2), 0, 1, 3, 50)); - assert_eq!(Balances::reserved_balance(&1), 0); + assert_ok!(Uniques::approve_transfer(Origin::signed(2), 0, 42, 3)); + assert_noop!(Uniques::cancel_approval(Origin::signed(2), 1, 42, None), Error::::Unknown); + assert_noop!(Uniques::cancel_approval(Origin::signed(2), 0, 43, None), Error::::Unknown); + assert_noop!(Uniques::cancel_approval(Origin::signed(3), 0, 42, None), Error::::NoPermission); + assert_noop!(Uniques::cancel_approval(Origin::signed(2), 0, 42, Some(4)), Error::::WrongDelegate); - assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); - assert_ok!(Uniques::cancel_approval(Origin::signed(1), 0, 2)); - assert_eq!(Balances::reserved_balance(&1), 0); + assert_ok!(Uniques::cancel_approval(Origin::signed(2), 0, 42, Some(3))); + assert_noop!(Uniques::cancel_approval(Origin::signed(2), 0, 42, None), Error::::NoDelegate); }); } #[test] -fn cancel_approval_works() { +fn cancel_approval_works_with_admin() { new_test_ext().execute_with(|| { assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - Balances::make_free_balance_be(&1, 1); - assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); - assert_noop!(Uniques::cancel_approval(Origin::signed(1), 1, 2), Error::::Unknown); - assert_noop!(Uniques::cancel_approval(Origin::signed(2), 0, 2), Error::::Unknown); - assert_noop!(Uniques::cancel_approval(Origin::signed(1), 0, 3), Error::::Unknown); - assert_ok!(Uniques::cancel_approval(Origin::signed(1), 0, 2)); - assert_noop!(Uniques::cancel_approval(Origin::signed(1), 0, 2), Error::::Unknown); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 2)); + + assert_ok!(Uniques::approve_transfer(Origin::signed(2), 0, 42, 3)); + assert_noop!(Uniques::cancel_approval(Origin::signed(1), 1, 42, None), Error::::Unknown); + assert_noop!(Uniques::cancel_approval(Origin::signed(1), 0, 43, None), Error::::Unknown); + assert_noop!(Uniques::cancel_approval(Origin::signed(1), 0, 42, Some(4)), Error::::WrongDelegate); + + assert_ok!(Uniques::cancel_approval(Origin::signed(1), 0, 42, Some(3))); + assert_noop!(Uniques::cancel_approval(Origin::signed(1), 0, 42, None), Error::::NoDelegate); }); } #[test] -fn force_cancel_approval_works() { +fn cancel_approval_works_with_force() { new_test_ext().execute_with(|| { assert_ok!(Uniques::force_create(Origin::root(), 0, 1, true)); - assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - Balances::make_free_balance_be(&1, 1); - assert_ok!(Uniques::approve_transfer(Origin::signed(1), 0, 2, 50)); - let e = Error::::NoPermission; - assert_noop!(Uniques::force_cancel_approval(Origin::signed(2), 0, 1, 2), e); - assert_noop!(Uniques::force_cancel_approval(Origin::signed(1), 1, 1, 2), Error::::Unknown); - assert_noop!(Uniques::force_cancel_approval(Origin::signed(1), 0, 2, 2), Error::::Unknown); - assert_noop!(Uniques::force_cancel_approval(Origin::signed(1), 0, 1, 3), Error::::Unknown); - assert_ok!(Uniques::force_cancel_approval(Origin::signed(1), 0, 1, 2)); - assert_noop!(Uniques::force_cancel_approval(Origin::signed(1), 0, 1, 2), Error::::Unknown); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 2)); + + assert_ok!(Uniques::approve_transfer(Origin::signed(2), 0, 42, 3)); + assert_noop!(Uniques::cancel_approval(Origin::root(), 1, 42, None), Error::::Unknown); + assert_noop!(Uniques::cancel_approval(Origin::root(), 0, 43, None), Error::::Unknown); + assert_noop!(Uniques::cancel_approval(Origin::root(), 0, 42, Some(4)), Error::::WrongDelegate); + + assert_ok!(Uniques::cancel_approval(Origin::root(), 0, 42, Some(3))); + assert_noop!(Uniques::cancel_approval(Origin::root(), 0, 42, None), Error::::NoDelegate); }); } -*/ diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index ca4ddc6377038..cd8db19258247 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -59,7 +59,6 @@ pub trait WeightInfo { fn set_team() -> Weight; fn approve_transfer() -> Weight; fn cancel_approval() -> Weight; - fn force_cancel_approval() -> Weight; fn force_asset_status() -> Weight; fn set_metadata(n: u32, i: u32, ) -> Weight; fn clear_metadata() -> Weight; @@ -153,11 +152,6 @@ impl WeightInfo for SubstrateWeight { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn force_cancel_approval() -> Weight { - (54_879_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } fn force_asset_status() -> Weight { (23_366_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) @@ -276,11 +270,6 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn force_cancel_approval() -> Weight { - (54_879_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } fn force_asset_status() -> Weight { (23_366_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) From de1eb5a9be73db0966cf61ecfa0fcf1ba773704d Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Thu, 20 May 2021 09:05:19 +0200 Subject: [PATCH 29/54] Update impl_fungibles.rs --- frame/assets/src/impl_fungibles.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/assets/src/impl_fungibles.rs b/frame/assets/src/impl_fungibles.rs index 256ba5939a77f..d0ab13072a88d 100644 --- a/frame/assets/src/impl_fungibles.rs +++ b/frame/assets/src/impl_fungibles.rs @@ -19,7 +19,7 @@ use super::*; -impl, I: 'static> non_fungibles::Inspect<::AccountId> for Pallet { +impl, I: 'static> fungibles::Inspect<::AccountId> for Pallet { type AssetId = T::AssetId; type Balance = T::Balance; From 53c666ceb0f9353901869ace77e43e99d0a5ba6a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 20 May 2021 09:06:47 +0200 Subject: [PATCH 30/54] Initial benchmarking --- frame/assets/src/lib.rs | 1 + frame/uniques/src/benchmarking.rs | 140 ++++++++++++++---------------- frame/uniques/src/lib.rs | 12 ++- frame/uniques/src/weights.rs | 30 +++---- 4 files changed, 85 insertions(+), 98 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 80269e14083ed..75c195de5945f 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -133,6 +133,7 @@ mod tests; mod extra_mutator; pub use extra_mutator::*; mod impl_fungibles; +mod impl_stored_map; mod functions; mod types; pub use types::*; diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index 0d80ec5923d26..7c17cc7f0036b 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -29,94 +29,76 @@ use frame_benchmarking::{ use frame_support::traits::Get; use frame_support::{traits::EnsureOrigin, dispatch::UnfilteredDispatchable}; -use crate::Pallet as Assets; +use crate::Pallet as Uniques; const SEED: u32 = 0; -fn create_default_asset, I: 'static>(is_sufficient: bool) +fn create_class, I: 'static>() -> (T::AccountId, ::Source) { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); - let root = SystemOrigin::Root.into(); - assert!(Assets::::force_create( - root, + T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); + assert!(Uniques::::create( + SystemOrigin::Signed(caller.clone()).into(), Default::default(), caller_lookup.clone(), - is_sufficient, - 1u32.into(), ).is_ok()); (caller, caller_lookup) } -fn create_default_minted_asset, I: 'static>(is_sufficient: bool, amount: T::Balance) +fn add_class_metadata, I: 'static>() -> (T::AccountId, ::Source) { - let (caller, caller_lookup) = create_default_asset::(is_sufficient); - if !is_sufficient { - T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); + let caller = Class::::get(T::ClassId::default()).unwrap().owner; + if caller != whitelisted_caller() { + whitelist_account!(caller); } - assert!(Assets::::mint( + let caller_lookup = T::Lookup::unlookup(caller.clone()); + assert!(Uniques::::set_class_metadata( SystemOrigin::Signed(caller.clone()).into(), Default::default(), - caller_lookup.clone(), - amount, + vec![0; T::StringLimit::get() as usize], + vec![0; T::StringLimit::get() as usize], + false, ).is_ok()); (caller, caller_lookup) } -fn swap_is_sufficient, I: 'static>(s: &mut bool) { - Asset::::mutate(&T::AssetId::default(), |maybe_a| - if let Some(ref mut a) = maybe_a { sp_std::mem::swap(s, &mut a.is_sufficient) } - ); -} - -fn add_consumers, I: 'static>(minter: T::AccountId, n: u32) { - let origin = SystemOrigin::Signed(minter); - let mut s = false; - swap_is_sufficient::(&mut s); - for i in 0..n { - let target = account("consumer", i, SEED); - T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); - let target_lookup = T::Lookup::unlookup(target); - assert!(Assets::::mint(origin.clone().into(), Default::default(), target_lookup, 100u32.into()).is_ok()); +fn mint_instance, I: 'static>(index: u16) + -> (T::AccountId, ::Source) +{ + let caller = Class::::get(T::ClassId::default()).unwrap().admin; + if caller != whitelisted_caller() { + whitelist_account!(caller); } - swap_is_sufficient::(&mut s); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + assert!(Uniques::::mint( + SystemOrigin::Signed(caller.clone()).into(), + Default::default(), + index.into(), + caller_lookup.clone(), + ).is_ok()); + (caller, caller_lookup) } -fn add_sufficients, I: 'static>(minter: T::AccountId, n: u32) { - let origin = SystemOrigin::Signed(minter); - let mut s = true; - swap_is_sufficient::(&mut s); - for i in 0..n { - let target = account("sufficient", i, SEED); - let target_lookup = T::Lookup::unlookup(target); - assert!(Assets::::mint(origin.clone().into(), Default::default(), target_lookup, 100u32.into()).is_ok()); +fn add_instance_metadata, I: 'static>(index: u16) + -> (T::AccountId, ::Source) +{ + let caller = Class::::get(T::ClassId::default()).unwrap().owner; + if caller != whitelisted_caller() { + whitelist_account!(caller); } - swap_is_sufficient::(&mut s); -} - -fn add_approvals, I: 'static>(minter: T::AccountId, n: u32) { - T::Currency::deposit_creating(&minter, T::ApprovalDeposit::get() * n.into()); - let minter_lookup = T::Lookup::unlookup(minter.clone()); - let origin = SystemOrigin::Signed(minter); - Assets::::mint( - origin.clone().into(), + let caller_lookup = T::Lookup::unlookup(caller.clone()); + assert!(Uniques::::set_metadata( + SystemOrigin::Signed(caller.clone()).into(), Default::default(), - minter_lookup, - (100 * (n + 1)).into(), - ).unwrap(); - for i in 0..n { - let target = account("approval", i, SEED); - T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); - let target_lookup = T::Lookup::unlookup(target); - Assets::::approve_transfer( - origin.clone().into(), - Default::default(), - target_lookup, - 100u32.into(), - ).unwrap(); - } + index.into(), + vec![0; T::StringLimit::get() as usize], + vec![0; T::StringLimit::get() as usize], + false, + ).is_ok()); + (caller, caller_lookup) } fn assert_last_event, I: 'static>(generic_event: >::Event) { @@ -126,7 +108,7 @@ fn assert_last_event, I: 'static>(generic_event: >:: let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; assert_eq!(event, &system_event); } - +/* fn assert_event, I: 'static>(generic_event: >::Event) { let system_event: ::Event = generic_event.into(); let events = frame_system::Pallet::::events(); @@ -134,13 +116,13 @@ fn assert_event, I: 'static>(generic_event: >::Event matches!(&event_record, frame_system::EventRecord { event, .. } if &system_event == event) })); } - +*/ benchmarks_instance_pallet! { create { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, 1u32.into()) + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup) verify { assert_last_event::(Event::Created(Default::default(), caller.clone(), caller).into()); } @@ -148,25 +130,31 @@ benchmarks_instance_pallet! { force_create { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); - }: _(SystemOrigin::Root, Default::default(), caller_lookup, true, 1u32.into()) + }: _(SystemOrigin::Root, Default::default(), caller_lookup, true) verify { assert_last_event::(Event::ForceCreated(Default::default(), caller).into()); } destroy { - let c in 0 .. 5_000; - let s in 0 .. 5_000; - let a in 0 .. 5_00; - let (caller, _) = create_default_asset::(true); - add_consumers::(caller.clone(), c); - add_sufficients::(caller.clone(), s); - add_approvals::(caller.clone(), a); - let witness = Asset::::get(T::AssetId::default()).unwrap().destroy_witness(); + let n in 0 .. 5_000; + let m in 0 .. 5_000; + + let (caller, _) = create_class::(); + add_class_metadata::(); + for i in 0..n + m { + // create instance + mint_instance::(i as u16); + if i < m { + // add metadata + add_instance_metadata::(i as u16); + } + } + let witness = Class::::get(T::ClassId::default()).unwrap().destroy_witness(); }: _(SystemOrigin::Signed(caller), Default::default(), witness) verify { assert_last_event::(Event::Destroyed(Default::default()).into()); } - +/* mint { let (caller, caller_lookup) = create_default_asset::(true); let amount = T::Balance::from(100u32); @@ -423,7 +411,7 @@ benchmarks_instance_pallet! { }: _(SystemOrigin::Signed(caller.clone()), id, caller_lookup, delegate_lookup) verify { assert_last_event::(Event::ApprovalCancelled(id, caller, delegate).into()); - } + }*/ } -impl_benchmark_test_suite!(Assets, crate::mock::new_test_ext(), crate::mock::Test); +impl_benchmark_test_suite!(Uniques, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index a93318a4459f1..609b9edaaef84 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -28,10 +28,8 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod weights; -/* TODO: #[cfg(feature = "runtime-benchmarks")] mod benchmarking; -*/ #[cfg(test)] pub mod mock; #[cfg(test)] @@ -69,7 +67,7 @@ pub mod pallet { type ClassId: Member + Parameter + Default + Copy + HasCompact; /// The type used to identify a unique asset within an asset class. - type InstanceId: Member + Parameter + Default + Copy + HasCompact; + type InstanceId: Member + Parameter + Default + Copy + HasCompact + From; /// The currency mechanism, used for paying for reserves. type Currency: ReservableCurrency; @@ -351,11 +349,11 @@ pub mod pallet { /// /// Emits `Destroyed` event when successful. /// - /// Weight: `O(i + f)` where: - /// - `i = witness.instances` - /// - `f = witness.instance_metdadatas` + /// Weight: `O(n + m)` where: + /// - `n = witness.instances - witness.instance_metdadatas` + /// - `m = witness.instance_metdadatas` #[pallet::weight(T::WeightInfo::destroy( - witness.instances, + witness.instances.saturating_sub(witness.instance_metadatas), witness.instance_metadatas, ))] pub(super) fn destroy( diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index cd8db19258247..9f60d256888e1 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -46,7 +46,7 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; - fn destroy(c: u32, s: u32, ) -> Weight; + fn destroy(n: u32, m: u32, ) -> Weight; fn mint() -> Weight; fn burn() -> Weight; fn transfer() -> Weight; @@ -79,18 +79,18 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn destroy(c: u32, s: u32, ) -> Weight { + fn destroy(n: u32, m: u32, ) -> Weight { (0 as Weight) // Standard Error: 38_000 - .saturating_add((24_232_000 as Weight).saturating_mul(c as Weight)) + .saturating_add((24_232_000 as Weight).saturating_mul(n as Weight)) // Standard Error: 38_000 - .saturating_add((30_467_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((30_467_000 as Weight).saturating_mul(m as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(c as Weight))) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(s as Weight))) + .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(m as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) - .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(c as Weight))) - .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(s as Weight))) + .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(m as Weight))) } fn mint() -> Weight { (46_433_000 as Weight) @@ -197,18 +197,18 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn destroy(c: u32, s: u32, ) -> Weight { + fn destroy(n: u32, m: u32, ) -> Weight { (0 as Weight) // Standard Error: 38_000 - .saturating_add((24_232_000 as Weight).saturating_mul(c as Weight)) + .saturating_add((24_232_000 as Weight).saturating_mul(n as Weight)) // Standard Error: 38_000 - .saturating_add((30_467_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((30_467_000 as Weight).saturating_mul(m as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(c as Weight))) - .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(s as Weight))) + .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(n as Weight))) + .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(m as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(c as Weight))) - .saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(s as Weight))) + .saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(n as Weight))) + .saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(m as Weight))) } fn mint() -> Weight { (46_433_000 as Weight) From 1545abe1f6f3bd9620ef71ecee14cc3bceefa58e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 20 May 2021 11:31:48 +0200 Subject: [PATCH 31/54] benchmark --- Cargo.lock | 1 + bin/node/runtime/Cargo.toml | 4 + bin/node/runtime/src/lib.rs | 23 ++- frame/assets/src/impl_fungibles.rs | 8 +- frame/assets/src/lib.rs | 2 +- frame/uniques/src/benchmarking.rs | 309 ++++++++++++----------------- frame/uniques/src/lib.rs | 29 ++- 7 files changed, 173 insertions(+), 203 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 07db654167136..581dfabc1bb30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4331,6 +4331,7 @@ dependencies = [ "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-treasury", + "pallet-uniques", "pallet-utility", "pallet-vesting", "parity-scale-codec", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 512f32d66a66c..7038bfacc6b4b 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -85,6 +85,7 @@ pallet-treasury = { version = "3.0.0", default-features = false, path = "../../. pallet-utility = { version = "3.0.0", default-features = false, path = "../../../frame/utility" } pallet-transaction-payment = { version = "3.0.0", default-features = false, path = "../../../frame/transaction-payment" } pallet-transaction-payment-rpc-runtime-api = { version = "3.0.0", default-features = false, path = "../../../frame/transaction-payment/rpc/runtime-api/" } +pallet-uniques = { version = "3.0.0", default-features = false, path = "../../../frame/uniques" } pallet-vesting = { version = "3.0.0", default-features = false, path = "../../../frame/vesting" } [build-dependencies] @@ -155,6 +156,7 @@ std = [ "sp-version/std", "pallet-society/std", "pallet-recovery/std", + "pallet-uniques/std", "pallet-vesting/std", "log/std", "frame-try-runtime/std", @@ -191,6 +193,7 @@ runtime-benchmarks = [ "pallet-tips/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-utility/runtime-benchmarks", + "pallet-uniques/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", "pallet-offences-benchmarking", "pallet-session-benchmarking", @@ -234,6 +237,7 @@ try-runtime = [ "pallet-utility/try-runtime", "pallet-society/try-runtime", "pallet-recovery/try-runtime", + "pallet-uniques/try-runtime", "pallet-vesting/try-runtime", "pallet-gilt/try-runtime", ] diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a570329dcfce3..8c33844c36ba0 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1089,6 +1089,25 @@ impl pallet_gilt::Config for Runtime { type WeightInfo = pallet_gilt::weights::SubstrateWeight; } +parameter_types! { + pub const ClassDeposit: Balance = 100 * DOLLARS; + pub const InstanceDeposit: Balance = 1 * DOLLARS; +} + +impl pallet_uniques::Config for Runtime { + type Event = Event; + type ClassId = u32; + type InstanceId = u32; + type Currency = Balances; + type ForceOrigin = frame_system::EnsureRoot; + type ClassDeposit = ClassDeposit; + type InstanceDeposit = InstanceDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type StringLimit = StringLimit; + type WeightInfo = pallet_uniques::weights::SubstrateWeight; +} + construct_runtime!( pub enum Runtime where Block = Block, @@ -1133,6 +1152,7 @@ construct_runtime!( Mmr: pallet_mmr::{Pallet, Storage}, Lottery: pallet_lottery::{Pallet, Call, Storage, Event}, Gilt: pallet_gilt::{Pallet, Call, Storage, Event, Config}, + Uniques: pallet_uniques::{Pallet, Call, Storage, Event}, } ); @@ -1507,8 +1527,9 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_timestamp, Timestamp); add_benchmark!(params, batches, pallet_tips, Tips); add_benchmark!(params, batches, pallet_treasury, Treasury); + add_benchmark!(params, batches, pallet_uniques, Uniques); add_benchmark!(params, batches, pallet_utility, Utility); - add_benchmark!(params, batches, pallet_vesting, Vesting); + add_benchmark!(params, batches, pallet_uniques, Vesting); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) diff --git a/frame/assets/src/impl_fungibles.rs b/frame/assets/src/impl_fungibles.rs index d0ab13072a88d..71951bae11165 100644 --- a/frame/assets/src/impl_fungibles.rs +++ b/frame/assets/src/impl_fungibles.rs @@ -127,26 +127,26 @@ impl, I: 'static> fungibles::Unbalanced for Pallet Result + -> Result { let f = DebitFlags { keep_alive: false, best_effort: false }; Self::decrease_balance(asset, who, amount, f, |_, _| Ok(())) } fn decrease_balance_at_most(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance) - -> Self::Balance + -> Self::Balance { let f = DebitFlags { keep_alive: false, best_effort: true }; Self::decrease_balance(asset, who, amount, f, |_, _| Ok(())) .unwrap_or(Zero::zero()) } fn increase_balance(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance) - -> Result + -> Result { Self::increase_balance(asset, who, amount, |_| Ok(()))?; Ok(amount) } fn increase_balance_at_most(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance) - -> Self::Balance + -> Self::Balance { match Self::increase_balance(asset, who, amount, |_| Ok(())) { Ok(()) => amount, diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 75c195de5945f..0996588ee1388 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -132,8 +132,8 @@ mod tests; mod extra_mutator; pub use extra_mutator::*; -mod impl_fungibles; mod impl_stored_map; +mod impl_fungibles; mod functions; mod types; pub use types::*; diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index 7c17cc7f0036b..0fa36ad52f711 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -26,25 +26,25 @@ use frame_system::RawOrigin as SystemOrigin; use frame_benchmarking::{ benchmarks_instance_pallet, account, whitelisted_caller, whitelist_account, impl_benchmark_test_suite }; -use frame_support::traits::Get; -use frame_support::{traits::EnsureOrigin, dispatch::UnfilteredDispatchable}; +use frame_support::{traits::{Get, EnsureOrigin}, dispatch::UnfilteredDispatchable}; use crate::Pallet as Uniques; const SEED: u32 = 0; fn create_class, I: 'static>() - -> (T::AccountId, ::Source) + -> (T::ClassId, T::AccountId, ::Source) { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); + let class = Default::default(); T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); assert!(Uniques::::create( SystemOrigin::Signed(caller.clone()).into(), - Default::default(), + class, caller_lookup.clone(), ).is_ok()); - (caller, caller_lookup) + (class, caller, caller_lookup) } fn add_class_metadata, I: 'static>() @@ -66,23 +66,24 @@ fn add_class_metadata, I: 'static>() } fn mint_instance, I: 'static>(index: u16) - -> (T::AccountId, ::Source) + -> (T::InstanceId, T::AccountId, ::Source) { let caller = Class::::get(T::ClassId::default()).unwrap().admin; if caller != whitelisted_caller() { whitelist_account!(caller); } let caller_lookup = T::Lookup::unlookup(caller.clone()); + let instance = index.into(); assert!(Uniques::::mint( SystemOrigin::Signed(caller.clone()).into(), Default::default(), - index.into(), + instance, caller_lookup.clone(), ).is_ok()); - (caller, caller_lookup) + (instance, caller, caller_lookup) } -fn add_instance_metadata, I: 'static>(index: u16) +fn add_instance_metadata, I: 'static>(instance: T::InstanceId) -> (T::AccountId, ::Source) { let caller = Class::::get(T::ClassId::default()).unwrap().owner; @@ -93,7 +94,7 @@ fn add_instance_metadata, I: 'static>(index: u16) assert!(Uniques::::set_metadata( SystemOrigin::Signed(caller.clone()).into(), Default::default(), - index.into(), + instance, vec![0; T::StringLimit::get() as usize], vec![0; T::StringLimit::get() as usize], false, @@ -139,279 +140,213 @@ benchmarks_instance_pallet! { let n in 0 .. 5_000; let m in 0 .. 5_000; - let (caller, _) = create_class::(); + let (class, caller, _) = create_class::(); add_class_metadata::(); for i in 0..n + m { // create instance - mint_instance::(i as u16); + let (instance, ..) = mint_instance::(i as u16); if i < m { // add metadata - add_instance_metadata::(i as u16); + add_instance_metadata::(instance); } } - let witness = Class::::get(T::ClassId::default()).unwrap().destroy_witness(); - }: _(SystemOrigin::Signed(caller), Default::default(), witness) + let witness = Class::::get(class).unwrap().destroy_witness(); + }: _(SystemOrigin::Signed(caller), class, witness) verify { - assert_last_event::(Event::Destroyed(Default::default()).into()); + assert_last_event::(Event::Destroyed(class).into()); } -/* + mint { - let (caller, caller_lookup) = create_default_asset::(true); - let amount = T::Balance::from(100u32); - }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, amount) + let (class, caller, caller_lookup) = create_class::(); + let instance = Default::default(); + }: _(SystemOrigin::Signed(caller.clone()), class, instance, caller_lookup) verify { - assert_last_event::(Event::Issued(Default::default(), caller, amount).into()); + assert_last_event::(Event::Issued(class, instance, caller).into()); } burn { - let amount = T::Balance::from(100u32); - let (caller, caller_lookup) = create_default_minted_asset::(true, amount); - }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, amount) + let (class, caller, caller_lookup) = create_class::(); + let (instance, ..) = mint_instance::(0); + add_instance_metadata::(instance); + }: _(SystemOrigin::Signed(caller.clone()), class, instance, Some(caller_lookup)) verify { - assert_last_event::(Event::Burned(Default::default(), caller, amount).into()); + assert_last_event::(Event::Burned(class, instance, caller).into()); } transfer { - let amount = T::Balance::from(100u32); - let (caller, caller_lookup) = create_default_minted_asset::(true, amount); - let target: T::AccountId = account("target", 0, SEED); - let target_lookup = T::Lookup::unlookup(target.clone()); - }: _(SystemOrigin::Signed(caller.clone()), Default::default(), target_lookup, amount) - verify { - assert_last_event::(Event::Transferred(Default::default(), caller, target, amount).into()); - } + let (class, caller, caller_lookup) = create_class::(); + let (instance, ..) = mint_instance::(Default::default()); - transfer_keep_alive { - let mint_amount = T::Balance::from(200u32); - let amount = T::Balance::from(100u32); - let (caller, caller_lookup) = create_default_minted_asset::(true, mint_amount); let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - }: _(SystemOrigin::Signed(caller.clone()), Default::default(), target_lookup, amount) + }: _(SystemOrigin::Signed(caller.clone()), class, instance, target_lookup) verify { - assert!(frame_system::Pallet::::account_exists(&caller)); - assert_last_event::(Event::Transferred(Default::default(), caller, target, amount).into()); + assert_last_event::(Event::Transferred(class, instance, caller, target).into()); } - force_transfer { - let amount = T::Balance::from(100u32); - let (caller, caller_lookup) = create_default_minted_asset::(true, amount); - let target: T::AccountId = account("target", 0, SEED); - let target_lookup = T::Lookup::unlookup(target.clone()); - }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, target_lookup, amount) + redeposit { + let i in 0 .. 5_000; + let (class, caller, caller_lookup) = create_class::(); + let instances = (0..i).map(|x| mint_instance::(x as u16).0).collect::>(); + Uniques::::force_asset_status( + SystemOrigin::Root.into(), + class, + caller_lookup.clone(), + caller_lookup.clone(), + caller_lookup.clone(), + caller_lookup.clone(), + true, + false, + )?; + }: _(SystemOrigin::Signed(caller.clone()), class, instances.clone()) verify { - assert_last_event::( - Event::Transferred(Default::default(), caller, target, amount).into() - ); + assert_last_event::(Event::Redeposited(class, instances).into()); } freeze { - let (caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup) + let (class, caller, caller_lookup) = create_class::(); + let (instance, ..) = mint_instance::(Default::default()); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), Default::default()) verify { - assert_last_event::(Event::Frozen(Default::default(), caller).into()); + assert_last_event::(Event::Frozen(Default::default(), Default::default()).into()); } thaw { - let (caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - Assets::::freeze( + let (class, caller, caller_lookup) = create_class::(); + let (instance, ..) = mint_instance::(Default::default()); + Uniques::::freeze( SystemOrigin::Signed(caller.clone()).into(), - Default::default(), - caller_lookup.clone(), + class, + instance, )?; - }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup) + }: _(SystemOrigin::Signed(caller.clone()), class, instance) verify { - assert_last_event::(Event::Thawed(Default::default(), caller).into()); + assert_last_event::(Event::Thawed(class, instance).into()); } - freeze_asset { - let (caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - }: _(SystemOrigin::Signed(caller.clone()), Default::default()) + freeze_class { + let (class, caller, caller_lookup) = create_class::(); + }: _(SystemOrigin::Signed(caller.clone()), class) verify { - assert_last_event::(Event::AssetFrozen(Default::default()).into()); + assert_last_event::(Event::ClassFrozen(class).into()); } - thaw_asset { - let (caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - Assets::::freeze_asset( - SystemOrigin::Signed(caller.clone()).into(), - Default::default(), - )?; - }: _(SystemOrigin::Signed(caller.clone()), Default::default()) + thaw_class { + let (class, caller, caller_lookup) = create_class::(); + let origin = SystemOrigin::Signed(caller.clone()).into(); + Uniques::::freeze_class(origin, class)?; + }: _(SystemOrigin::Signed(caller.clone()), class) verify { - assert_last_event::(Event::AssetThawed(Default::default()).into()); + assert_last_event::(Event::ClassThawed(class).into()); } transfer_ownership { - let (caller, _) = create_default_asset::(true); + let (class, caller, _) = create_class::(); let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - }: _(SystemOrigin::Signed(caller), Default::default(), target_lookup) + T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); + }: _(SystemOrigin::Signed(caller), class, target_lookup) verify { - assert_last_event::(Event::OwnerChanged(Default::default(), target).into()); + assert_last_event::(Event::OwnerChanged(class, target).into()); } set_team { - let (caller, _) = create_default_asset::(true); + let (class, caller, _) = create_class::(); 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), Default::default(), target0.clone(), target1.clone(), target2.clone()) verify { assert_last_event::(Event::TeamChanged( - Default::default(), + class, account("target", 0, SEED), account("target", 1, SEED), account("target", 2, SEED), ).into()); } + force_asset_status { + let (class, caller, caller_lookup) = create_class::(); + let origin = T::ForceOrigin::successful_origin(); + let call = Call::::force_asset_status( + class, + caller_lookup.clone(), + caller_lookup.clone(), + caller_lookup.clone(), + caller_lookup.clone(), + true, + false, + ); + }: { call.dispatch_bypass_filter(origin)? } + verify { + assert_last_event::(Event::AssetStatusChanged(class).into()); + } + set_metadata { let n in 0 .. T::StringLimit::get(); - let s in 0 .. T::StringLimit::get(); + let i in 0 .. T::StringLimit::get(); let name = vec![0u8; n as usize]; - let symbol = vec![0u8; s as usize]; - let decimals = 12; + let info = vec![0u8; i as usize]; - let (caller, _) = create_default_asset::(true); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - }: _(SystemOrigin::Signed(caller), Default::default(), name.clone(), symbol.clone(), decimals) + let (class, caller, _) = create_class::(); + let (instance, ..) = mint_instance::(0); + }: _(SystemOrigin::Signed(caller), class, instance, name.clone(), info.clone(), false) verify { - let id = Default::default(); - assert_last_event::(Event::MetadataSet(id, name, symbol, decimals, false).into()); + assert_last_event::(Event::MetadataSet(class, instance, name, info, false).into()); } clear_metadata { - let (caller, _) = create_default_asset::(true); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - let dummy = vec![0u8; T::StringLimit::get() as usize]; - let origin = SystemOrigin::Signed(caller.clone()).into(); - Assets::::set_metadata(origin, Default::default(), dummy.clone(), dummy, 12)?; - }: _(SystemOrigin::Signed(caller), Default::default()) + let (class, caller, _) = create_class::(); + let (instance, ..) = mint_instance::(0); + add_instance_metadata::(instance); + }: _(SystemOrigin::Signed(caller), class, instance) verify { - assert_last_event::(Event::MetadataCleared(Default::default()).into()); + assert_last_event::(Event::MetadataCleared(class, instance).into()); } - force_set_metadata { + set_class_metadata { let n in 0 .. T::StringLimit::get(); - let s in 0 .. T::StringLimit::get(); + let i in 0 .. T::StringLimit::get(); let name = vec![0u8; n as usize]; - let symbol = vec![0u8; s as usize]; - let decimals = 12; + let info = vec![0u8; i as usize]; - create_default_asset::(true); - - let origin = T::ForceOrigin::successful_origin(); - let call = Call::::force_set_metadata( - Default::default(), - name.clone(), - symbol.clone(), - decimals, - false, - ); - }: { call.dispatch_bypass_filter(origin)? } - verify { - let id = Default::default(); - assert_last_event::(Event::MetadataSet(id, name, symbol, decimals, false).into()); - } - - force_clear_metadata { - let (caller, _) = create_default_asset::(true); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - let dummy = vec![0u8; T::StringLimit::get() as usize]; - let origin = SystemOrigin::Signed(caller.clone()).into(); - Assets::::set_metadata(origin, Default::default(), dummy.clone(), dummy, 12)?; - - let origin = T::ForceOrigin::successful_origin(); - let call = Call::::force_clear_metadata(Default::default()); - }: { call.dispatch_bypass_filter(origin)? } + let (class, caller, _) = create_class::(); + }: _(SystemOrigin::Signed(caller), class, name.clone(), info.clone(), false) verify { - assert_last_event::(Event::MetadataCleared(Default::default()).into()); + assert_last_event::(Event::ClassMetadataSet(class, name, info, false).into()); } - force_asset_status { - let (caller, caller_lookup) = create_default_asset::(true); - - let origin = T::ForceOrigin::successful_origin(); - let call = Call::::force_asset_status( - Default::default(), - caller_lookup.clone(), - caller_lookup.clone(), - caller_lookup.clone(), - caller_lookup.clone(), - 100u32.into(), - true, - false, - ); - }: { call.dispatch_bypass_filter(origin)? } + clear_class_metadata { + let (class, caller, _) = create_class::(); + add_class_metadata::(); + }: _(SystemOrigin::Signed(caller), class) verify { - assert_last_event::(Event::AssetStatusChanged(Default::default()).into()); + assert_last_event::(Event::ClassMetadataCleared(class).into()); } approve_transfer { - let (caller, _) = create_default_minted_asset::(true, 100u32.into()); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - - let id = Default::default(); - let delegate: T::AccountId = account("delegate", 0, SEED); - let delegate_lookup = T::Lookup::unlookup(delegate.clone()); - let amount = 100u32.into(); - }: _(SystemOrigin::Signed(caller.clone()), id, delegate_lookup, amount) - verify { - assert_last_event::(Event::ApprovedTransfer(id, caller, delegate, amount).into()); - } - - transfer_approved { - let (owner, owner_lookup) = create_default_minted_asset::(true, 100u32.into()); - T::Currency::make_free_balance_be(&owner, DepositBalanceOf::::max_value()); - - let id = Default::default(); + let (class, caller, _) = create_class::(); + let (instance, ..) = mint_instance::(0); 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::::approve_transfer(origin, id, delegate_lookup.clone(), amount)?; - - let dest: T::AccountId = account("dest", 0, SEED); - let dest_lookup = T::Lookup::unlookup(dest.clone()); - }: _(SystemOrigin::Signed(delegate.clone()), id, owner_lookup, dest_lookup, amount) + }: _(SystemOrigin::Signed(caller.clone()), class, instance, delegate_lookup) verify { - assert!(T::Currency::reserved_balance(&owner).is_zero()); - assert_event::(Event::Transferred(id, owner, dest, amount).into()); + assert_last_event::(Event::ApprovedTransfer(class, instance, caller, delegate).into()); } cancel_approval { - let (caller, _) = create_default_minted_asset::(true, 100u32.into()); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - - let id = Default::default(); + let (class, caller, _) = create_class::(); + let (instance, ..) = mint_instance::(0); 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::::approve_transfer(origin, id, delegate_lookup.clone(), amount)?; - }: _(SystemOrigin::Signed(caller.clone()), id, delegate_lookup) + Uniques::::approve_transfer(origin, class, instance, delegate_lookup.clone())?; + }: _(SystemOrigin::Signed(caller.clone()), class, instance, Some(delegate_lookup)) verify { - assert_last_event::(Event::ApprovalCancelled(id, caller, delegate).into()); + assert_last_event::(Event::ApprovalCancelled(class, instance, caller, delegate).into()); } - - force_cancel_approval { - let (caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - - let id = Default::default(); - 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::::approve_transfer(origin, id, delegate_lookup.clone(), amount)?; - }: _(SystemOrigin::Signed(caller.clone()), id, caller_lookup, delegate_lookup) - verify { - assert_last_event::(Event::ApprovalCancelled(id, caller, delegate).into()); - }*/ } impl_benchmark_test_suite!(Uniques, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 609b9edaaef84..b7925d6ff3596 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -163,7 +163,7 @@ pub mod pallet { pub enum Event, I: 'static = ()> { /// Some asset class was created. \[class, creator, owner\] Created(T::ClassId, T::AccountId, T::AccountId), - /// Some asset class was force-created. \[asset_id, owner\] + /// Some asset class was force-created. \[class, owner\] ForceCreated(T::ClassId, T::AccountId), /// An asset class was destroyed. Destroyed(T::ClassId), @@ -196,14 +196,17 @@ pub mod pallet { /// An asset `class` has had its attributes changed by the `Force` origin. /// \[ class \] AssetStatusChanged(T::ClassId), - /// New metadata has been set for an asset class. \[ asset_id, name, info, is_frozen \] + /// New metadata has been set for an asset class. \[ class, name, info, is_frozen \] ClassMetadataSet(T::ClassId, Vec, Vec, bool), - /// Metadata has been cleared for an asset class. \[ asset_id \] + /// Metadata has been cleared for an asset class. \[ class \] ClassMetadataCleared(T::ClassId), - /// New metadata has been set for an asset instance. \[ asset_id, name, info, is_frozen \] - MetadataSet(T::ClassId, Vec, Vec, bool), - /// Metadata has been cleared for an asset instance. \[ asset_id \] - MetadataCleared(T::ClassId), + /// New metadata has been set for an asset instance. + /// \[ class, instance, name, info, is_frozen \] + MetadataSet(T::ClassId, T::InstanceId, Vec, Vec, bool), + /// Metadata has been cleared for an asset instance. \[ class, instance \] + MetadataCleared(T::ClassId, T::InstanceId), + /// Metadata has been cleared for an asset instance. \[ class, successful_instances \] + Redeposited(T::ClassId, Vec), } #[pallet::error] @@ -566,6 +569,7 @@ pub mod pallet { false => T::InstanceDeposit::get(), }; + let mut successful = Vec::with_capacity(instances.len()); for instance in instances.into_iter() { let mut details = match Asset::::get(&class, &instance) { Some(x) => x, @@ -580,14 +584,19 @@ pub mod pallet { // this is OK to do. continue } + } else { + continue } class_details.total_deposit.saturating_accrue(deposit); class_details.total_deposit.saturating_reduce(old); details.deposit = deposit; Asset::::insert(&class, &instance, &details); + successful.push(instance); } Class::::insert(&class, &class_details); + Self::deposit_event(Event::::Redeposited(class, successful)); + Ok(()) } @@ -647,7 +656,7 @@ pub mod pallet { details.is_frozen = false; Asset::::insert(&class, &instance, &details); - Self::deposit_event(Event::::Frozen(class, instance)); + Self::deposit_event(Event::::Thawed(class, instance)); Ok(()) } @@ -989,7 +998,7 @@ pub mod pallet { }); Class::::insert(&class, &class_details); - Self::deposit_event(Event::MetadataSet(class, name, info, is_frozen)); + Self::deposit_event(Event::MetadataSet(class, instance, name, info, is_frozen)); Ok(()) }) } @@ -1035,7 +1044,7 @@ pub mod pallet { class_details.total_deposit.saturating_reduce(deposit); Class::::insert(&class, &class_details); - Self::deposit_event(Event::MetadataCleared(class)); + Self::deposit_event(Event::MetadataCleared(class, instance)); Ok(()) }) } From 24e08ccca016adb539b141ab16e74d726d2f6763 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 20 May 2021 11:40:30 +0200 Subject: [PATCH 32/54] Fixes --- bin/node/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 8c33844c36ba0..b26dd14d15cd2 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1529,7 +1529,7 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_treasury, Treasury); add_benchmark!(params, batches, pallet_uniques, Uniques); add_benchmark!(params, batches, pallet_utility, Utility); - add_benchmark!(params, batches, pallet_uniques, Vesting); + add_benchmark!(params, batches, pallet_vesting, Vesting); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) From da6f748ee1c9d4e84df87ce76f1b5fabbead4568 Mon Sep 17 00:00:00 2001 From: Parity Bot Date: Thu, 20 May 2021 10:52:49 +0000 Subject: [PATCH 33/54] cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_uniques --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/uniques/src/weights.rs --template=./.maintain/frame-weight-template.hbs --- frame/uniques/src/weights.rs | 236 ++++++++++++++++++----------------- 1 file changed, 122 insertions(+), 114 deletions(-) diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 9f60d256888e1..59f001a22f01a 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -15,10 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_assets +//! Autogenerated weights for pallet_uniques //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-03-08, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-05-20, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -27,12 +27,12 @@ // --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_assets +// --pallet=pallet_uniques // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/assets/src/weights.rs +// --output=./frame/uniques/src/weights.rs // --template=./.maintain/frame-weight-template.hbs @@ -42,7 +42,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use sp_std::marker::PhantomData; -/// Weight functions needed for pallet_assets. +/// Weight functions needed for pallet_uniques. pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; @@ -57,129 +57,133 @@ pub trait WeightInfo { fn thaw_class() -> Weight; fn transfer_ownership() -> Weight; fn set_team() -> Weight; - fn approve_transfer() -> Weight; - fn cancel_approval() -> Weight; fn force_asset_status() -> Weight; fn set_metadata(n: u32, i: u32, ) -> Weight; fn clear_metadata() -> Weight; fn set_class_metadata(n: u32, i: u32, ) -> Weight; fn clear_class_metadata() -> Weight; + fn approve_transfer() -> Weight; + fn cancel_approval() -> Weight; } -/// Weights for pallet_assets using the Substrate node and recommended hardware. +/// Weights for pallet_uniques using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn create() -> Weight { - (48_305_000 as Weight) + (54_481_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_create() -> Weight { - (23_827_000 as Weight) + (27_606_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn destroy(n: u32, m: u32, ) -> Weight { (0 as Weight) - // Standard Error: 38_000 - .saturating_add((24_232_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 38_000 - .saturating_add((30_467_000 as Weight).saturating_mul(m as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(n as Weight))) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(m as Weight))) + // Standard Error: 67_000 + .saturating_add((25_896_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 67_000 + .saturating_add((27_935_000 as Weight).saturating_mul(m as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(m as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(n as Weight))) - .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(m as Weight))) + .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(m as Weight))) } fn mint() -> Weight { - (46_433_000 as Weight) + (73_474_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn burn() -> Weight { - (46_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) + (104_472_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn transfer() -> Weight { - (70_793_000 as Weight) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) + (54_676_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn redeposit(_i: u32, ) -> Weight { - (70_968_000 as Weight) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) + fn redeposit(i: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 16_000 + .saturating_add((34_798_000 as Weight).saturating_mul(i as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(i as Weight))) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight))) } fn freeze() -> Weight { - (34_290_000 as Weight) + (39_555_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn thaw() -> Weight { - (34_419_000 as Weight) + (39_836_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn freeze_class() -> Weight { - (24_373_000 as Weight) + (28_985_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn thaw_class() -> Weight { - (24_096_000 as Weight) + (28_591_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn transfer_ownership() -> Weight { - (28_566_000 as Weight) + (66_000_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn set_team() -> Weight { - (25_297_000 as Weight) + (30_149_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn approve_transfer() -> Weight { - (47_906_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - fn cancel_approval() -> Weight { - (48_591_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } fn force_asset_status() -> Weight { - (23_366_000 as Weight) + (29_515_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_metadata(n: u32, i: u32, ) -> Weight { - (27_117_000 as Weight) + (74_491_000 as Weight) // Standard Error: 0 - .saturating_add((5_000 as Weight).saturating_mul(n as Weight)) - .saturating_add((5_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add((1_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 0 + .saturating_add((8_000 as Weight).saturating_mul(i as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn clear_metadata() -> Weight { - (51_598_000 as Weight) + (66_725_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn set_class_metadata(n: u32, i: u32, ) -> Weight { - (53_367_000 as Weight) + fn set_class_metadata(_n: u32, i: u32, ) -> Weight { + (66_832_000 as Weight) // Standard Error: 0 - .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) - .saturating_add((8_000 as Weight).saturating_mul(i as Weight)) + .saturating_add((4_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn clear_class_metadata() -> Weight { - (51_721_000 as Weight) + (60_690_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn approve_transfer() -> Weight { + (41_010_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn cancel_approval() -> Weight { + (41_092_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -188,116 +192,120 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn create() -> Weight { - (48_305_000 as Weight) + (54_481_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_create() -> Weight { - (23_827_000 as Weight) + (27_606_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn destroy(n: u32, m: u32, ) -> Weight { (0 as Weight) - // Standard Error: 38_000 - .saturating_add((24_232_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 38_000 - .saturating_add((30_467_000 as Weight).saturating_mul(m as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(n as Weight))) - .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(m as Weight))) + // Standard Error: 67_000 + .saturating_add((25_896_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 67_000 + .saturating_add((27_935_000 as Weight).saturating_mul(m as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(m as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) .saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(n as Weight))) - .saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(m as Weight))) + .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(m as Weight))) } fn mint() -> Weight { - (46_433_000 as Weight) + (73_474_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn burn() -> Weight { - (46_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + (104_472_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn transfer() -> Weight { - (70_793_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + (54_676_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn redeposit(_i: u32, ) -> Weight { - (70_968_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + fn redeposit(i: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 16_000 + .saturating_add((34_798_000 as Weight).saturating_mul(i as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(i as Weight))) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(i as Weight))) } fn freeze() -> Weight { - (34_290_000 as Weight) + (39_555_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn thaw() -> Weight { - (34_419_000 as Weight) + (39_836_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn freeze_class() -> Weight { - (24_373_000 as Weight) + (28_985_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn thaw_class() -> Weight { - (24_096_000 as Weight) + (28_591_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn transfer_ownership() -> Weight { - (28_566_000 as Weight) + (66_000_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn set_team() -> Weight { - (25_297_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - fn approve_transfer() -> Weight { - (47_906_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - fn cancel_approval() -> Weight { - (48_591_000 as Weight) + (30_149_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_asset_status() -> Weight { - (23_366_000 as Weight) + (29_515_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_metadata(n: u32, i: u32, ) -> Weight { - (27_117_000 as Weight) + (74_491_000 as Weight) // Standard Error: 0 - .saturating_add((5_000 as Weight).saturating_mul(n as Weight)) - .saturating_add((5_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add((1_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 0 + .saturating_add((8_000 as Weight).saturating_mul(i as Weight)) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn clear_metadata() -> Weight { - (51_598_000 as Weight) + (66_725_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - fn set_class_metadata(n: u32, i: u32, ) -> Weight { - (53_367_000 as Weight) + fn set_class_metadata(_n: u32, i: u32, ) -> Weight { + (66_832_000 as Weight) // Standard Error: 0 - .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) - .saturating_add((8_000 as Weight).saturating_mul(i as Weight)) + .saturating_add((4_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn clear_class_metadata() -> Weight { - (51_721_000 as Weight) + (60_690_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn approve_transfer() -> Weight { + (41_010_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn cancel_approval() -> Weight { + (41_092_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } From 95e49b9232aba3aa4e697f28bb546a44befaf560 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 20 May 2021 13:12:27 +0200 Subject: [PATCH 34/54] Attributes --- frame/uniques/src/benchmarking.rs | 26 ++++++- frame/uniques/src/lib.rs | 108 ++++++++++++++++++++++++++++++ frame/uniques/src/types.rs | 6 ++ frame/uniques/src/weights.rs | 23 ++++++- 4 files changed, 159 insertions(+), 4 deletions(-) diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index 0fa36ad52f711..bd15dd4e7e242 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -139,8 +139,9 @@ benchmarks_instance_pallet! { destroy { let n in 0 .. 5_000; let m in 0 .. 5_000; + let a in 0 .. 5_000; - let (class, caller, _) = create_class::(); + let (class, caller, caller_lookup) = create_class::(); add_class_metadata::(); for i in 0..n + m { // create instance @@ -150,6 +151,15 @@ benchmarks_instance_pallet! { add_instance_metadata::(instance); } } + for i in 0..a { + assert!(Uniques::::set_attribute( + SystemOrigin::Signed(caller.clone()).into(), + class, + Some((i as u16).into()), + vec![0; T::StringLimit::get() as usize], + Some(vec![0; T::StringLimit::get() as usize]), + ).is_ok()); + } let witness = Class::::get(class).unwrap().destroy_witness(); }: _(SystemOrigin::Signed(caller), class, witness) verify { @@ -282,6 +292,20 @@ benchmarks_instance_pallet! { assert_last_event::(Event::AssetStatusChanged(class).into()); } + set_attribute { + let k in 0 .. T::StringLimit::get(); + let v in 0 .. T::StringLimit::get(); + + let key = vec![0u8; k as usize]; + let value = vec![0u8; v as usize]; + + let (class, caller, _) = create_class::(); + let (instance, ..) = mint_instance::(0); + }: _(SystemOrigin::Signed(caller), class, Some(instance), key.clone(), Some(value.clone())) + verify { + assert_last_event::(Event::AttributeSet(class, Some(instance), key, Some(value)).into()); + } + set_metadata { let n in 0 .. T::StringLimit::get(); let i in 0 .. T::StringLimit::get(); diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index b7925d6ff3596..279367fcbf035 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -153,6 +153,19 @@ pub mod pallet { OptionQuery, >; + #[pallet::storage] + /// Metadata of an asset class. + pub(super) type Attribute, I: 'static = ()> = StorageNMap< + _, + ( + NMapKey, + NMapKey>, + NMapKey>, + ), + (Vec, DepositBalanceOf), + OptionQuery + >; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] #[pallet::metadata( @@ -207,6 +220,9 @@ pub mod pallet { MetadataCleared(T::ClassId, T::InstanceId), /// Metadata has been cleared for an asset instance. \[ class, successful_instances \] Redeposited(T::ClassId, Vec), + /// New metadata has been set for an asset instance. + /// \[ class, maybe_instance, key, maybe_value \] + AttributeSet(T::ClassId, Option, Vec, Option>), } #[pallet::error] @@ -288,6 +304,7 @@ pub mod pallet { free_holding: false, instances: 0, instance_metadatas: 0, + attributes: 0, is_frozen: false, }, ); @@ -334,6 +351,7 @@ pub mod pallet { free_holding, instances: 0, instance_metadatas: 0, + attributes: 0, is_frozen: false, }, ); @@ -358,6 +376,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::destroy( witness.instances.saturating_sub(witness.instance_metadatas), witness.instance_metadatas, + witness.attributes, ))] pub(super) fn destroy( origin: OriginFor, @@ -375,12 +394,14 @@ pub mod pallet { } ensure!(class_details.instances == witness.instances, Error::::BadWitness); ensure!(class_details.instance_metadatas == witness.instance_metadatas, Error::::BadWitness); + ensure!(class_details.attributes == witness.attributes, Error::::BadWitness); for (instance, details) in Asset::::drain_prefix(&class) { Account::::remove((&details.owner, &class, &instance)); } InstanceMetadataOf::::remove_prefix(&class); ClassMetadataOf::::remove(&class); + Attribute::::remove_prefix((&class,)); T::Currency::unreserve(&class_details.owner, class_details.total_deposit); Self::deposit_event(Event::Destroyed(class)); @@ -925,6 +946,93 @@ pub mod pallet { }) } + /// Set an attribute for an asset class or instance. + /// + /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the + /// asset `class`. + /// + /// If the origin is Signed, then funds of signer are reserved according to the formula: + /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + info.len)` taking into + /// account any already reserved funds. + /// + /// - `class`: The identifier of the asset class whose instance's metadata to set. + /// - `instance`: The identifier of the asset instance whose metadata to set. + /// - `name`: The user visible name of this asset. Limited in length by `StringLimit`. + /// - `info`: The general information of this asset. Limited in length by `StringLimit`. + /// - `is_frozen`: Whether the metadata should be frozen against further changes. + /// + /// Emits `MetadataSet`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::set_attribute( + key.len() as u32, + maybe_value.as_ref().map_or(0, |v| v.len()) as u32, + ))] + pub(super) fn set_attribute( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + maybe_instance: Option, + key: Vec, + maybe_value: Option>, + ) -> DispatchResult { + let maybe_check_owner = T::ForceOrigin::try_origin(origin) + .map(|_| None) + .or_else(|origin| ensure_signed(origin).map(Some))?; + + ensure!(key.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + if let Some(ref value) = maybe_value { + ensure!(value.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + } + + let mut class_details = Class::::get(&class) + .ok_or(Error::::Unknown)?; + + if let Some(check_owner) = &maybe_check_owner { + ensure!(check_owner == &class_details.owner, Error::::NoPermission); + } + + let frozen = match maybe_instance { + Some(instance) => + InstanceMetadataOf::::get(class, instance).map(|v| v.is_frozen), + None => + ClassMetadataOf::::get(class).map(|v| v.is_frozen), + }.unwrap_or(false); + ensure!(!frozen, Error::::Frozen); + + let attribute = Attribute::::get((class, maybe_instance, &key)); + if attribute.is_none() && maybe_value.is_some() { + class_details.attributes.saturating_inc(); + } + if attribute.is_some() && maybe_value.is_none() { + class_details.attributes.saturating_dec(); + } + let old_deposit = attribute.map_or(Zero::zero(), |m| m.1); + class_details.total_deposit.saturating_reduce(old_deposit); + let mut deposit = Zero::zero(); + if let Some(ref value) = maybe_value { + if !class_details.free_holding && maybe_check_owner.is_some() { + deposit = T::MetadataDepositPerByte::get() + .saturating_mul(((key.len() + value.len()) as u32).into()) + .saturating_add(T::MetadataDepositBase::get()); + } + class_details.total_deposit.saturating_accrue(deposit); + } + if deposit > old_deposit { + T::Currency::reserve(&class_details.owner, deposit - old_deposit)?; + } else if deposit < old_deposit { + T::Currency::unreserve(&class_details.owner, old_deposit - deposit); + } + + match maybe_value { + None => Attribute::::remove((class, maybe_instance, &key)), + Some(ref value) => + Attribute::::insert((&class, maybe_instance, &key), (value, deposit)), + } + Class::::insert(class, &class_details); + Self::deposit_event(Event::AttributeSet(class, maybe_instance, key, maybe_value)); + Ok(()) + } + /// Set the metadata for an asset instance. /// /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the diff --git a/frame/uniques/src/types.rs b/frame/uniques/src/types.rs index fb742ae7ade2c..ccdb0faa62ba1 100644 --- a/frame/uniques/src/types.rs +++ b/frame/uniques/src/types.rs @@ -44,6 +44,8 @@ pub struct ClassDetails< pub(super) instances: u32, /// The total number of outstanding instance metadata of this asset class. pub(super) instance_metadatas: u32, + /// The total number of attributes for this asset class. + pub(super) attributes: u32, /// Whether the asset is frozen for non-admin transfers. pub(super) is_frozen: bool, } @@ -57,6 +59,9 @@ pub struct DestroyWitness { /// The total number of outstanding instance metadata of this asset class. #[codec(compact)] pub(super) instance_metadatas: u32, + #[codec(compact)] + /// The total number of attributes for this asset class. + pub(super) attributes: u32, } impl ClassDetails { @@ -64,6 +69,7 @@ impl ClassDetails { DestroyWitness { instances: self.instances, instance_metadatas: self.instance_metadatas, + attributes: self.attributes, } } } diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 9f60d256888e1..9fa547a73c8c8 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -46,7 +46,7 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; - fn destroy(n: u32, m: u32, ) -> Weight; + fn destroy(n: u32, m: u32, _a: u32, ) -> Weight; fn mint() -> Weight; fn burn() -> Weight; fn transfer() -> Weight; @@ -60,6 +60,7 @@ pub trait WeightInfo { fn approve_transfer() -> Weight; fn cancel_approval() -> Weight; fn force_asset_status() -> Weight; + fn set_attribute(k: u32, v: u32, ) -> Weight; fn set_metadata(n: u32, i: u32, ) -> Weight; fn clear_metadata() -> Weight; fn set_class_metadata(n: u32, i: u32, ) -> Weight; @@ -79,7 +80,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn destroy(n: u32, m: u32, ) -> Weight { + fn destroy(n: u32, m: u32, _a: u32, ) -> Weight { (0 as Weight) // Standard Error: 38_000 .saturating_add((24_232_000 as Weight).saturating_mul(n as Weight)) @@ -157,6 +158,14 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } + fn set_attribute(k: u32, v: u32, ) -> Weight { + (27_117_000 as Weight) + // Standard Error: 0 + .saturating_add((5_000 as Weight).saturating_mul(k as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } fn set_metadata(n: u32, i: u32, ) -> Weight { (27_117_000 as Weight) // Standard Error: 0 @@ -197,7 +206,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn destroy(n: u32, m: u32, ) -> Weight { + fn destroy(n: u32, m: u32, _a: u32, ) -> Weight { (0 as Weight) // Standard Error: 38_000 .saturating_add((24_232_000 as Weight).saturating_mul(n as Weight)) @@ -275,6 +284,14 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } + fn set_attribute(k: u32, v: u32, ) -> Weight { + (27_117_000 as Weight) + // Standard Error: 0 + .saturating_add((5_000 as Weight).saturating_mul(k as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } fn set_metadata(n: u32, i: u32, ) -> Weight { (27_117_000 as Weight) // Standard Error: 0 From ccaf6ecaf9664bef1db92df7e2cf697bf9f9072e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 20 May 2021 16:04:37 +0100 Subject: [PATCH 35/54] Attribute metadata --- bin/node/runtime/src/lib.rs | 7 +- frame/uniques/src/benchmarking.rs | 58 +++++++---- frame/uniques/src/lib.rs | 159 ++++++++++++++++++------------ frame/uniques/src/mock.rs | 8 +- frame/uniques/src/tests.rs | 81 +++++++++++++-- frame/uniques/src/weights.rs | 43 ++++---- 6 files changed, 246 insertions(+), 110 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index b26dd14d15cd2..e6348728af72a 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1092,6 +1092,8 @@ impl pallet_gilt::Config for Runtime { parameter_types! { pub const ClassDeposit: Balance = 100 * DOLLARS; pub const InstanceDeposit: Balance = 1 * DOLLARS; + pub const KeyLimit: u32 = 32; + pub const ValueLimit: u32 = 256; } impl pallet_uniques::Config for Runtime { @@ -1103,8 +1105,11 @@ impl pallet_uniques::Config for Runtime { type ClassDeposit = ClassDeposit; type InstanceDeposit = InstanceDeposit; type MetadataDepositBase = MetadataDepositBase; - type MetadataDepositPerByte = MetadataDepositPerByte; + type AttributeDepositBase = MetadataDepositBase; + type DepositPerByte = MetadataDepositPerByte; type StringLimit = StringLimit; + type KeyLimit = KeyLimit; + type ValueLimit = ValueLimit; type WeightInfo = pallet_uniques::weights::SubstrateWeight; } diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index bd15dd4e7e242..734eb40848612 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -102,6 +102,25 @@ fn add_instance_metadata, I: 'static>(instance: T::InstanceId) (caller, caller_lookup) } +fn add_instance_attribute, I: 'static>(instance: T::InstanceId) + -> (Vec, T::AccountId, ::Source) +{ + let caller = Class::::get(T::ClassId::default()).unwrap().owner; + if caller != whitelisted_caller() { + whitelist_account!(caller); + } + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let key = vec![0; T::KeyLimit::get() as usize]; + assert!(Uniques::::set_attribute( + SystemOrigin::Signed(caller.clone()).into(), + Default::default(), + Some(instance), + key.clone(), + vec![0; T::ValueLimit::get() as usize], + ).is_ok()); + (key, caller, caller_lookup) +} + fn assert_last_event, I: 'static>(generic_event: >::Event) { let events = frame_system::Pallet::::events(); let system_event: ::Event = generic_event.into(); @@ -156,8 +175,8 @@ benchmarks_instance_pallet! { SystemOrigin::Signed(caller.clone()).into(), class, Some((i as u16).into()), - vec![0; T::StringLimit::get() as usize], - Some(vec![0; T::StringLimit::get() as usize]), + vec![0; T::KeyLimit::get() as usize], + vec![0; T::ValueLimit::get() as usize], ).is_ok()); } let witness = Class::::get(class).unwrap().destroy_witness(); @@ -177,7 +196,6 @@ benchmarks_instance_pallet! { burn { let (class, caller, caller_lookup) = create_class::(); let (instance, ..) = mint_instance::(0); - add_instance_metadata::(instance); }: _(SystemOrigin::Signed(caller.clone()), class, instance, Some(caller_lookup)) verify { assert_last_event::(Event::Burned(class, instance, caller).into()); @@ -293,25 +311,30 @@ benchmarks_instance_pallet! { } set_attribute { - let k in 0 .. T::StringLimit::get(); - let v in 0 .. T::StringLimit::get(); + let key = vec![0u8; T::KeyLimit::get() as usize]; + let value = vec![0u8; T::ValueLimit::get() as usize]; - let key = vec![0u8; k as usize]; - let value = vec![0u8; v as usize]; + let (class, caller, _) = create_class::(); + let (instance, ..) = mint_instance::(0); + add_instance_metadata::(instance); + }: _(SystemOrigin::Signed(caller), class, Some(instance), key.clone(), value.clone()) + verify { + assert_last_event::(Event::AttributeSet(class, Some(instance), key, value).into()); + } + clear_attribute { let (class, caller, _) = create_class::(); let (instance, ..) = mint_instance::(0); - }: _(SystemOrigin::Signed(caller), class, Some(instance), key.clone(), Some(value.clone())) + add_instance_metadata::(instance); + let (key, ..) = add_instance_attribute::(instance); + }: _(SystemOrigin::Signed(caller), class, Some(instance), key.clone()) verify { - assert_last_event::(Event::AttributeSet(class, Some(instance), key, Some(value)).into()); + assert_last_event::(Event::AttributeCleared(class, Some(instance), key).into()); } set_metadata { - let n in 0 .. T::StringLimit::get(); - let i in 0 .. T::StringLimit::get(); - - let name = vec![0u8; n as usize]; - let info = vec![0u8; i as usize]; + let name = vec![0u8; T::StringLimit::get() as usize]; + let info = vec![0u8; T::StringLimit::get() as usize]; let (class, caller, _) = create_class::(); let (instance, ..) = mint_instance::(0); @@ -330,11 +353,8 @@ benchmarks_instance_pallet! { } set_class_metadata { - let n in 0 .. T::StringLimit::get(); - let i in 0 .. T::StringLimit::get(); - - let name = vec![0u8; n as usize]; - let info = vec![0u8; i as usize]; + let name = vec![0u8; T::StringLimit::get() as usize]; + let info = vec![0u8; T::StringLimit::get() as usize]; let (class, caller, _) = create_class::(); }: _(SystemOrigin::Signed(caller), class, name.clone(), info.clone(), false) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 279367fcbf035..450a6de6784b9 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -85,13 +85,22 @@ pub mod pallet { /// The basic amount of funds that must be reserved when adding metadata to your asset. type MetadataDepositBase: Get>; - /// The additional funds that must be reserved for the number of bytes you store in your - /// metadata. - type MetadataDepositPerByte: Get>; + /// The basic amount of funds that must be reserved when adding an attribute to an asset. + type AttributeDepositBase: Get>; + + /// The additional funds that must be reserved for the number of bytes store in metadata, + /// either "normal" metadata or attribute metadata. + type DepositPerByte: Get>; /// The maximum length of a name or symbol stored on-chain. type StringLimit: Get; + /// The maximum length of an attribute key. + type KeyLimit: Get; + + /// The maximum length of an attribute value. + type ValueLimit: Get; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -220,9 +229,12 @@ pub mod pallet { MetadataCleared(T::ClassId, T::InstanceId), /// Metadata has been cleared for an asset instance. \[ class, successful_instances \] Redeposited(T::ClassId, Vec), - /// New metadata has been set for an asset instance. + /// New attribute metadata has been set for an asset class or instance. + /// \[ class, maybe_instance, key, value \] + AttributeSet(T::ClassId, Option, Vec, Vec), + /// Attribute metadata has been cleared for an asset class or instance. /// \[ class, maybe_instance, key, maybe_value \] - AttributeSet(T::ClassId, Option, Vec, Option>), + AttributeCleared(T::ClassId, Option, Vec), } #[pallet::error] @@ -371,10 +383,11 @@ pub mod pallet { /// Emits `Destroyed` event when successful. /// /// Weight: `O(n + m)` where: - /// - `n = witness.instances - witness.instance_metdadatas` + /// - `n = witness.instances` /// - `m = witness.instance_metdadatas` + /// - `a = witness.attributes` #[pallet::weight(T::WeightInfo::destroy( - witness.instances.saturating_sub(witness.instance_metadatas), + witness.instances, witness.instance_metadatas, witness.attributes, ))] @@ -495,13 +508,6 @@ pub mod pallet { T::Currency::unreserve(&class_details.owner, details.deposit); class_details.total_deposit.saturating_reduce(details.deposit); class_details.instances.saturating_dec(); - - if let Some(metadata) = InstanceMetadataOf::::take(&class, &instance) { - // Remove instance metadata - T::Currency::unreserve(&class_details.owner, metadata.deposit); - class_details.total_deposit.saturating_reduce(metadata.deposit); - class_details.instance_metadatas.saturating_dec(); - } Ok(details.owner) })?; @@ -952,84 +958,115 @@ pub mod pallet { /// asset `class`. /// /// If the origin is Signed, then funds of signer are reserved according to the formula: - /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + info.len)` taking into + /// `MetadataDepositBase + DepositPerByte * (key.len + value.len)` taking into /// account any already reserved funds. /// /// - `class`: The identifier of the asset class whose instance's metadata to set. /// - `instance`: The identifier of the asset instance whose metadata to set. - /// - `name`: The user visible name of this asset. Limited in length by `StringLimit`. - /// - `info`: The general information of this asset. Limited in length by `StringLimit`. - /// - `is_frozen`: Whether the metadata should be frozen against further changes. + /// - `key`: The key of the attribute. + /// - `value`: The value to which to set the attribute. /// - /// Emits `MetadataSet`. + /// Emits `AttributeSet`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::set_attribute( - key.len() as u32, - maybe_value.as_ref().map_or(0, |v| v.len()) as u32, - ))] + #[pallet::weight(T::WeightInfo::set_attribute())] pub(super) fn set_attribute( origin: OriginFor, #[pallet::compact] class: T::ClassId, maybe_instance: Option, key: Vec, - maybe_value: Option>, + value: Vec, ) -> DispatchResult { let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - ensure!(key.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); - if let Some(ref value) = maybe_value { - ensure!(value.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); - } - - let mut class_details = Class::::get(&class) - .ok_or(Error::::Unknown)?; + ensure!(key.len() <= T::KeyLimit::get() as usize, Error::::BadMetadata); + ensure!(value.len() <= T::ValueLimit::get() as usize, Error::::BadMetadata); + let mut class_details = Class::::get(&class).ok_or(Error::::Unknown)?; if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &class_details.owner, Error::::NoPermission); } - - let frozen = match maybe_instance { + let maybe_is_frozen = match maybe_instance { + None => ClassMetadataOf::::get(class).map(|v| v.is_frozen), Some(instance) => InstanceMetadataOf::::get(class, instance).map(|v| v.is_frozen), - None => - ClassMetadataOf::::get(class).map(|v| v.is_frozen), - }.unwrap_or(false); - ensure!(!frozen, Error::::Frozen); + }; + ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); let attribute = Attribute::::get((class, maybe_instance, &key)); - if attribute.is_none() && maybe_value.is_some() { + if attribute.is_none() { class_details.attributes.saturating_inc(); } - if attribute.is_some() && maybe_value.is_none() { - class_details.attributes.saturating_dec(); - } let old_deposit = attribute.map_or(Zero::zero(), |m| m.1); class_details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if let Some(ref value) = maybe_value { - if !class_details.free_holding && maybe_check_owner.is_some() { - deposit = T::MetadataDepositPerByte::get() - .saturating_mul(((key.len() + value.len()) as u32).into()) - .saturating_add(T::MetadataDepositBase::get()); - } - class_details.total_deposit.saturating_accrue(deposit); + if !class_details.free_holding && maybe_check_owner.is_some() { + deposit = T::DepositPerByte::get() + .saturating_mul(((key.len() + value.len()) as u32).into()) + .saturating_add(T::AttributeDepositBase::get()); } + class_details.total_deposit.saturating_accrue(deposit); if deposit > old_deposit { T::Currency::reserve(&class_details.owner, deposit - old_deposit)?; } else if deposit < old_deposit { T::Currency::unreserve(&class_details.owner, old_deposit - deposit); } - match maybe_value { - None => Attribute::::remove((class, maybe_instance, &key)), - Some(ref value) => - Attribute::::insert((&class, maybe_instance, &key), (value, deposit)), - } + Attribute::::insert((&class, maybe_instance, &key), (&value, deposit)); Class::::insert(class, &class_details); - Self::deposit_event(Event::AttributeSet(class, maybe_instance, key, maybe_value)); + Self::deposit_event(Event::AttributeSet(class, maybe_instance, key, value)); + Ok(()) + } + + /// Set an attribute for an asset class or instance. + /// + /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the + /// asset `class`. + /// + /// If the origin is Signed, then funds of signer are reserved according to the formula: + /// `MetadataDepositBase + DepositPerByte * (key.len + value.len)` taking into + /// account any already reserved funds. + /// + /// - `class`: The identifier of the asset class whose instance's metadata to set. + /// - `instance`: The identifier of the asset instance whose metadata to set. + /// - `key`: The key of the attribute. + /// - `value`: The value to which to set the attribute. + /// + /// Emits `AttributeSet`. + /// + /// Weight: `O(1)` + #[pallet::weight(T::WeightInfo::clear_attribute())] + pub(super) fn clear_attribute( + origin: OriginFor, + #[pallet::compact] class: T::ClassId, + maybe_instance: Option, + key: Vec, + ) -> DispatchResult { + let maybe_check_owner = T::ForceOrigin::try_origin(origin) + .map(|_| None) + .or_else(|origin| ensure_signed(origin).map(Some))?; + + ensure!(key.len() <= T::KeyLimit::get() as usize, Error::::BadMetadata); + let mut class_details = Class::::get(&class).ok_or(Error::::Unknown)?; + if let Some(check_owner) = &maybe_check_owner { + ensure!(check_owner == &class_details.owner, Error::::NoPermission); + } + let maybe_is_frozen = match maybe_instance { + None => ClassMetadataOf::::get(class).map(|v| v.is_frozen), + Some(instance) => + InstanceMetadataOf::::get(class, instance).map(|v| v.is_frozen), + }; + ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); + + if let Some((_, deposit)) = Attribute::::take((class, maybe_instance, &key)) { + class_details.attributes.saturating_dec(); + class_details.total_deposit.saturating_reduce(deposit); + T::Currency::unreserve(&class_details.owner, deposit); + Class::::insert(class, &class_details); + Self::deposit_event(Event::AttributeCleared(class, maybe_instance, key)); + } Ok(()) } @@ -1039,7 +1076,7 @@ pub mod pallet { /// asset `class`. /// /// If the origin is Signed, then funds of signer are reserved according to the formula: - /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + info.len)` taking into + /// `MetadataDepositBase + DepositPerByte * (name.len + info.len)` taking into /// account any already reserved funds. /// /// - `class`: The identifier of the asset class whose instance's metadata to set. @@ -1051,7 +1088,7 @@ pub mod pallet { /// Emits `MetadataSet`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::set_metadata(name.len() as u32, info.len() as u32))] + #[pallet::weight(T::WeightInfo::set_metadata())] pub(super) fn set_metadata( origin: OriginFor, #[pallet::compact] class: T::ClassId, @@ -1074,8 +1111,6 @@ pub mod pallet { ensure!(check_owner == &class_details.owner, Error::::NoPermission); } - ensure!(Asset::::contains_key(&class, &instance), Error::::Unknown); - InstanceMetadataOf::::try_mutate_exists(class, instance, |metadata| { let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); @@ -1087,7 +1122,7 @@ pub mod pallet { class_details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); if !class_details.free_holding && maybe_check_owner.is_some() { - deposit = T::MetadataDepositPerByte::get() + deposit = T::DepositPerByte::get() .saturating_mul(((name.len() + info.len()) as u32).into()) .saturating_add(T::MetadataDepositBase::get()); } @@ -1163,7 +1198,7 @@ pub mod pallet { /// the asset `class`. /// /// If the origin is `Signed`, then funds of signer are reserved according to the formula: - /// `MetadataDepositBase + MetadataDepositPerByte * name.len` taking into + /// `MetadataDepositBase + DepositPerByte * name.len` taking into /// account any already reserved funds. /// /// - `class`: The identifier of the asset whose metadata to update. @@ -1173,7 +1208,7 @@ pub mod pallet { /// Emits `ClassMetadataSet`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::set_class_metadata(name.len() as u32, info.len() as u32))] + #[pallet::weight(T::WeightInfo::set_class_metadata())] pub(super) fn set_class_metadata( origin: OriginFor, #[pallet::compact] class: T::ClassId, @@ -1201,7 +1236,7 @@ pub mod pallet { details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); if maybe_check_owner.is_some() && !details.free_holding { - deposit = T::MetadataDepositPerByte::get() + deposit = T::DepositPerByte::get() .saturating_mul(((name.len() + info.len()) as u32).into()) .saturating_add(T::MetadataDepositBase::get()); } diff --git a/frame/uniques/src/mock.rs b/frame/uniques/src/mock.rs index e18053b1371c7..1040821d0d886 100644 --- a/frame/uniques/src/mock.rs +++ b/frame/uniques/src/mock.rs @@ -85,8 +85,11 @@ impl pallet_balances::Config for Test { parameter_types! { pub const ClassDeposit: u64 = 2; pub const InstanceDeposit: u64 = 1; + pub const KeyLimit: u32 = 50; + pub const ValueLimit: u32 = 50; pub const StringLimit: u32 = 50; pub const MetadataDepositBase: u64 = 1; + pub const AttributeDepositBase: u64 = 1; pub const MetadataDepositPerByte: u64 = 1; } @@ -99,8 +102,11 @@ impl Config for Test { type ClassDeposit = ClassDeposit; type InstanceDeposit = InstanceDeposit; type MetadataDepositBase = MetadataDepositBase; - type MetadataDepositPerByte = MetadataDepositPerByte; + type AttributeDepositBase = AttributeDepositBase; + type DepositPerByte = MetadataDepositPerByte; type StringLimit = StringLimit; + type KeyLimit = KeyLimit; + type ValueLimit = ValueLimit; type WeightInfo = (); } diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index 7f52acbe1e054..4a29e885cc14a 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -45,6 +45,12 @@ fn assets() -> Vec<(u64, u32, u32)> { r } +fn attributes(class: u32) -> Vec<(Option, Vec, Vec)> { + let mut s: Vec<_> = Attribute::::iter_prefix((class,)).map(|(k, v)| (k.0, k.1, v.0)).collect(); + s.sort(); + s +} + #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { @@ -288,10 +294,6 @@ fn set_instance_metadata_should_work() { // Cannot add metadata to unknown asset assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); - assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 10], false), - Error::::Unknown, - ); assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); // Cannot add metadata to unowned asset assert_noop!( @@ -346,6 +348,74 @@ fn set_instance_metadata_should_work() { }); } +#[test] +fn set_attribute_should_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); + + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, None, vec![0], vec![0])); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), vec![0], vec![0])); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), vec![1], vec![0])); + assert_eq!(attributes(0), vec![ + (None, vec![0], vec![0]), + (Some(0), vec![0], vec![0]), + (Some(0), vec![1], vec![0]), + ]); + assert_eq!(Balances::reserved_balance(1), 9); + + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, None, vec![0], vec![0; 10])); + assert_eq!(attributes(0), vec![ + (None, vec![0], vec![0; 10]), + (Some(0), vec![0], vec![0]), + (Some(0), vec![1], vec![0]), + ]); + assert_eq!(Balances::reserved_balance(1), 18); + + assert_ok!(Uniques::clear_attribute(Origin::signed(1), 0, Some(0), vec![1])); + assert_eq!(attributes(0), vec![ + (None, vec![0], vec![0; 10]), + (Some(0), vec![0], vec![0]), + ]); + assert_eq!(Balances::reserved_balance(1), 15); + + let w = Class::::get(0).unwrap().destroy_witness(); + assert_ok!(Uniques::destroy(Origin::signed(1), 0, w)); + assert_eq!(attributes(0), vec![]); + assert_eq!(Balances::reserved_balance(1), 0); + }); +} + +#[test] +fn set_attribute_should_respect_freeze() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + + assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); + + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, None, vec![0], vec![0])); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), vec![0], vec![0])); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(1), vec![0], vec![0])); + assert_eq!(attributes(0), vec![ + (None, vec![0], vec![0]), + (Some(0), vec![0], vec![0]), + (Some(1), vec![0], vec![0]), + ]); + assert_eq!(Balances::reserved_balance(1), 9); + + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![], vec![], true)); + let e = Error::::Frozen; + assert_noop!(Uniques::set_attribute(Origin::signed(1), 0, None, vec![0], vec![0]), e); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), vec![0], vec![1])); + + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 0, vec![], vec![], true)); + let e = Error::::Frozen; + assert_noop!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), vec![0], vec![1]), e); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(1), vec![0], vec![1])); + }); +} + #[test] fn force_asset_status_should_work(){ new_test_ext().execute_with(|| { @@ -392,8 +462,7 @@ fn burn_works() { assert_ok!(Uniques::mint(Origin::signed(2), 0, 42, 5)); assert_ok!(Uniques::mint(Origin::signed(2), 0, 69, 5)); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, vec![0; 1], vec![0; 1], false)); - assert_eq!(Balances::reserved_balance(1), 5); + assert_eq!(Balances::reserved_balance(1), 2); assert_noop!(Uniques::burn(Origin::signed(0), 0, 42, None), Error::::NoPermission); assert_noop!(Uniques::burn(Origin::signed(5), 0, 42, Some(6)), Error::::WrongOwner); diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 9fa547a73c8c8..625a60d061dbc 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -60,10 +60,11 @@ pub trait WeightInfo { fn approve_transfer() -> Weight; fn cancel_approval() -> Weight; fn force_asset_status() -> Weight; - fn set_attribute(k: u32, v: u32, ) -> Weight; - fn set_metadata(n: u32, i: u32, ) -> Weight; + fn set_attribute() -> Weight; + fn clear_attribute() -> Weight; + fn set_metadata() -> Weight; fn clear_metadata() -> Weight; - fn set_class_metadata(n: u32, i: u32, ) -> Weight; + fn set_class_metadata() -> Weight; fn clear_class_metadata() -> Weight; } @@ -158,19 +159,21 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn set_attribute(k: u32, v: u32, ) -> Weight { + fn set_attribute() -> Weight { (27_117_000 as Weight) // Standard Error: 0 - .saturating_add((5_000 as Weight).saturating_mul(k as Weight)) - .saturating_add((5_000 as Weight).saturating_mul(v as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn set_metadata(n: u32, i: u32, ) -> Weight { + fn clear_attribute() -> Weight { + (27_117_000 as Weight) + // Standard Error: 0 + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_metadata() -> Weight { (27_117_000 as Weight) // Standard Error: 0 - .saturating_add((5_000 as Weight).saturating_mul(n as Weight)) - .saturating_add((5_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -179,11 +182,9 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn set_class_metadata(n: u32, i: u32, ) -> Weight { + fn set_class_metadata() -> Weight { (53_367_000 as Weight) // Standard Error: 0 - .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) - .saturating_add((8_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -284,19 +285,21 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn set_attribute(k: u32, v: u32, ) -> Weight { + fn set_attribute() -> Weight { + (27_117_000 as Weight) + // Standard Error: 0 + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn clear_attribute() -> Weight { (27_117_000 as Weight) // Standard Error: 0 - .saturating_add((5_000 as Weight).saturating_mul(k as Weight)) - .saturating_add((5_000 as Weight).saturating_mul(v as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn set_metadata(n: u32, i: u32, ) -> Weight { + fn set_metadata() -> Weight { (27_117_000 as Weight) // Standard Error: 0 - .saturating_add((5_000 as Weight).saturating_mul(n as Weight)) - .saturating_add((5_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } @@ -305,11 +308,9 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn set_class_metadata(n: u32, i: u32, ) -> Weight { + fn set_class_metadata() -> Weight { (53_367_000 as Weight) // Standard Error: 0 - .saturating_add((8_000 as Weight).saturating_mul(n as Weight)) - .saturating_add((8_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } From c51153ff1aac84f9deac648c61e9881379313e34 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 20 May 2021 17:01:34 +0100 Subject: [PATCH 36/54] Fixes --- frame/uniques/src/benchmarking.rs | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index 734eb40848612..91d4d12afce7d 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -156,28 +156,20 @@ benchmarks_instance_pallet! { } destroy { - let n in 0 .. 5_000; - let m in 0 .. 5_000; - let a in 0 .. 5_000; + let n in 0 .. 1_000; + let m in 0 .. 1_000; + let a in 0 .. 1_000; let (class, caller, caller_lookup) = create_class::(); add_class_metadata::(); - for i in 0..n + m { - // create instance - let (instance, ..) = mint_instance::(i as u16); - if i < m { - // add metadata - add_instance_metadata::(instance); - } + for i in 0..n { + mint_instance::(i as u16); + } + for i in 0..m { + add_instance_metadata::((i as u16).into()); } for i in 0..a { - assert!(Uniques::::set_attribute( - SystemOrigin::Signed(caller.clone()).into(), - class, - Some((i as u16).into()), - vec![0; T::KeyLimit::get() as usize], - vec![0; T::ValueLimit::get() as usize], - ).is_ok()); + add_instance_attribute::((i as u16).into()); } let witness = Class::::get(class).unwrap().destroy_witness(); }: _(SystemOrigin::Signed(caller), class, witness) From 5361afbfaff6c0ba1132b5ed793ba5b75191a2be Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 21 May 2021 19:24:00 +0100 Subject: [PATCH 37/54] Update frame/uniques/README.md --- frame/uniques/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/uniques/README.md b/frame/uniques/README.md index f8583a5c91d70..9aa9df18ab2a6 100644 --- a/frame/uniques/README.md +++ b/frame/uniques/README.md @@ -1,4 +1,4 @@ -# Assets Module +# Uniques Module A simple, secure module for dealing with fungible assets. From 8a45594f4aa8e3655fd9493e0790d690002aa1f2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 21 May 2021 19:31:38 +0100 Subject: [PATCH 38/54] Docs --- frame/uniques/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 450a6de6784b9..fb518ffd68cd5 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -209,11 +209,11 @@ pub mod pallet { TeamChanged(T::ClassId, T::AccountId, T::AccountId, T::AccountId), /// An `instance` of an asset `class` has been approved by the `owner` for transfer by a /// `delegate`. - /// \[ clsss, instance, owner, delegate \] + /// \[ class, instance, owner, delegate \] ApprovedTransfer(T::ClassId, T::InstanceId, T::AccountId, T::AccountId), /// An approval for a `delegate` account to transfer the `instance` of an asset `class` was /// cancelled by its `owner`. - /// \[ clsss, instance, owner, delegate \] + /// \[ class, instance, owner, delegate \] ApprovalCancelled(T::ClassId, T::InstanceId, T::AccountId, T::AccountId), /// An asset `class` has had its attributes changed by the `Force` origin. /// \[ class \] From dad10a77465802000755161a666a45d7e7a33074 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 21 May 2021 19:33:41 +0100 Subject: [PATCH 39/54] Docs --- frame/uniques/src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index fb518ffd68cd5..7be3e4f7c8320 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -183,27 +183,27 @@ pub mod pallet { T::InstanceId = "InstanceId", )] pub enum Event, I: 'static = ()> { - /// Some asset class was created. \[class, creator, owner\] + /// An asset class was created. \[ class, creator, owner \] Created(T::ClassId, T::AccountId, T::AccountId), - /// Some asset class was force-created. \[class, owner\] + /// An asset class was force-created. \[ class, owner \] ForceCreated(T::ClassId, T::AccountId), - /// An asset class was destroyed. + /// An asset `class` was destroyed. \[ class \] Destroyed(T::ClassId), - /// Some assets were issued. \[class, instance, owner\] + /// An asset `instace` was issued. \[ class, instance, owner \] Issued(T::ClassId, T::InstanceId, T::AccountId), - /// Some assets were transferred. \[class, instance, from, to\] + /// An asset `instace` was transferred. \[ class, instance, from, to \] Transferred(T::ClassId, T::InstanceId, T::AccountId, T::AccountId), - /// Some assets were destroyed. \[class, instance, owner\] + /// An asset `instance` was destroyed. \[ class, instance, owner \] Burned(T::ClassId, T::InstanceId, T::AccountId), - /// Some account `who` was frozen. \[ class, instance \] + /// Some asset `instance` was frozen. \[ class, instance \] Frozen(T::ClassId, T::InstanceId), - /// Some account `who` was thawed. \[ class, instance \] + /// Some asset `instance` was thawed. \[ class, instance \] Thawed(T::ClassId, T::InstanceId), /// Some asset `class` was frozen. \[ class \] ClassFrozen(T::ClassId), /// Some asset `class` was thawed. \[ class \] ClassThawed(T::ClassId), - /// The owner changed \[ class, owner \] + /// The owner changed \[ class, new_owner \] OwnerChanged(T::ClassId, T::AccountId), /// The management team changed \[ class, issuer, admin, freezer \] TeamChanged(T::ClassId, T::AccountId, T::AccountId, T::AccountId), From 33fd4a8e9b3837242029d772f0f2fa1e37259412 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 21 May 2021 19:34:31 +0100 Subject: [PATCH 40/54] Docs --- frame/uniques/src/benchmarking.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index 91d4d12afce7d..423431b44a1bb 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -128,15 +128,7 @@ fn assert_last_event, I: 'static>(generic_event: >:: let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; assert_eq!(event, &system_event); } -/* -fn assert_event, I: 'static>(generic_event: >::Event) { - let system_event: ::Event = generic_event.into(); - let events = frame_system::Pallet::::events(); - assert!(events.iter().any(|event_record| { - matches!(&event_record, frame_system::EventRecord { event, .. } if &system_event == event) - })); -} -*/ + benchmarks_instance_pallet! { create { let caller: T::AccountId = whitelisted_caller(); From 31c6e038c9d044ddcbff1c4330ea6e1ddbc0da2b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 21 May 2021 19:43:34 +0100 Subject: [PATCH 41/54] Simple metadata --- frame/uniques/src/benchmarking.rs | 16 ++++----- frame/uniques/src/lib.rs | 35 ++++++++---------- frame/uniques/src/tests.rs | 60 ++++++++++++++----------------- frame/uniques/src/types.rs | 8 ++--- 4 files changed, 48 insertions(+), 71 deletions(-) diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index 423431b44a1bb..11870dbf32adc 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -59,7 +59,6 @@ fn add_class_metadata, I: 'static>() SystemOrigin::Signed(caller.clone()).into(), Default::default(), vec![0; T::StringLimit::get() as usize], - vec![0; T::StringLimit::get() as usize], false, ).is_ok()); (caller, caller_lookup) @@ -96,7 +95,6 @@ fn add_instance_metadata, I: 'static>(instance: T::InstanceId) Default::default(), instance, vec![0; T::StringLimit::get() as usize], - vec![0; T::StringLimit::get() as usize], false, ).is_ok()); (caller, caller_lookup) @@ -317,14 +315,13 @@ benchmarks_instance_pallet! { } set_metadata { - let name = vec![0u8; T::StringLimit::get() as usize]; - let info = vec![0u8; T::StringLimit::get() as usize]; + let data = vec![0u8; T::StringLimit::get() as usize]; let (class, caller, _) = create_class::(); let (instance, ..) = mint_instance::(0); - }: _(SystemOrigin::Signed(caller), class, instance, name.clone(), info.clone(), false) + }: _(SystemOrigin::Signed(caller), class, instance, data.clone(), false) verify { - assert_last_event::(Event::MetadataSet(class, instance, name, info, false).into()); + assert_last_event::(Event::MetadataSet(class, instance, data, false).into()); } clear_metadata { @@ -337,13 +334,12 @@ benchmarks_instance_pallet! { } set_class_metadata { - let name = vec![0u8; T::StringLimit::get() as usize]; - let info = vec![0u8; T::StringLimit::get() as usize]; + let data = vec![0u8; T::StringLimit::get() as usize]; let (class, caller, _) = create_class::(); - }: _(SystemOrigin::Signed(caller), class, name.clone(), info.clone(), false) + }: _(SystemOrigin::Signed(caller), class, data.clone(), false) verify { - assert_last_event::(Event::ClassMetadataSet(class, name, info, false).into()); + assert_last_event::(Event::ClassMetadataSet(class, data, false).into()); } clear_class_metadata { diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 7be3e4f7c8320..7cf55e1f70a12 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -218,13 +218,13 @@ pub mod pallet { /// An asset `class` has had its attributes changed by the `Force` origin. /// \[ class \] AssetStatusChanged(T::ClassId), - /// New metadata has been set for an asset class. \[ class, name, info, is_frozen \] - ClassMetadataSet(T::ClassId, Vec, Vec, bool), + /// New metadata has been set for an asset class. \[ class, data, is_frozen \] + ClassMetadataSet(T::ClassId, Vec, bool), /// Metadata has been cleared for an asset class. \[ class \] ClassMetadataCleared(T::ClassId), /// New metadata has been set for an asset instance. - /// \[ class, instance, name, info, is_frozen \] - MetadataSet(T::ClassId, T::InstanceId, Vec, Vec, bool), + /// \[ class, instance, data, is_frozen \] + MetadataSet(T::ClassId, T::InstanceId, Vec, bool), /// Metadata has been cleared for an asset instance. \[ class, instance \] MetadataCleared(T::ClassId, T::InstanceId), /// Metadata has been cleared for an asset instance. \[ class, successful_instances \] @@ -1081,8 +1081,7 @@ pub mod pallet { /// /// - `class`: The identifier of the asset class whose instance's metadata to set. /// - `instance`: The identifier of the asset instance whose metadata to set. - /// - `name`: The user visible name of this asset. Limited in length by `StringLimit`. - /// - `info`: The general information of this asset. Limited in length by `StringLimit`. + /// - `data`: The general information of this asset. Limited in length by `StringLimit`. /// - `is_frozen`: Whether the metadata should be frozen against further changes. /// /// Emits `MetadataSet`. @@ -1093,16 +1092,14 @@ pub mod pallet { origin: OriginFor, #[pallet::compact] class: T::ClassId, #[pallet::compact] instance: T::InstanceId, - name: Vec, - info: Vec, + data: Vec, is_frozen: bool, ) -> DispatchResult { let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - ensure!(name.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); - ensure!(info.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + ensure!(data.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); let mut class_details = Class::::get(&class) .ok_or(Error::::Unknown)?; @@ -1135,13 +1132,12 @@ pub mod pallet { *metadata = Some(InstanceMetadata { deposit, - name: name.clone(), - information: info.clone(), + data: data.clone(), is_frozen, }); Class::::insert(&class, &class_details); - Self::deposit_event(Event::MetadataSet(class, instance, name, info, is_frozen)); + Self::deposit_event(Event::MetadataSet(class, instance, data, is_frozen)); Ok(()) }) } @@ -1202,7 +1198,7 @@ pub mod pallet { /// account any already reserved funds. /// /// - `class`: The identifier of the asset whose metadata to update. - /// - `name`: The user visible name of this asset. Limited in length by `StringLimit`. + /// - `info`: The general information of this asset. Limited in length by `StringLimit`. /// - `is_frozen`: Whether the metadata should be frozen against further changes. /// /// Emits `ClassMetadataSet`. @@ -1212,16 +1208,14 @@ pub mod pallet { pub(super) fn set_class_metadata( origin: OriginFor, #[pallet::compact] class: T::ClassId, - name: Vec, - info: Vec, + data: Vec, is_frozen: bool, ) -> DispatchResult { let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - ensure!(name.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); - ensure!(info.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); + ensure!(data.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); let mut details = Class::::get(&class).ok_or(Error::::Unknown)?; if let Some(check_owner) = &maybe_check_owner { @@ -1251,12 +1245,11 @@ pub mod pallet { *metadata = Some(ClassMetadata { deposit, - name: name.clone(), - information: info.clone(), + data: data.clone(), is_frozen, }); - Self::deposit_event(Event::ClassMetadataSet(class, name, info, is_frozen)); + Self::deposit_event(Event::ClassMetadataSet(class, data, is_frozen)); Ok(()) }) } diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index 4a29e885cc14a..a8fe8fe5961c8 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -78,7 +78,7 @@ fn lifecycle_should_work() { assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_eq!(Balances::reserved_balance(&1), 2); - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0], vec![0], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0], false)); assert_eq!(Balances::reserved_balance(&1), 5); assert!(ClassMetadataOf::::contains_key(0)); @@ -90,10 +90,10 @@ fn lifecycle_should_work() { assert_eq!(Class::::get(0).unwrap().instances, 2); assert_eq!(Class::::get(0).unwrap().instance_metadatas, 0); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![42], vec![42], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![42], false)); assert_eq!(Balances::reserved_balance(&1), 10); assert!(InstanceMetadataOf::::contains_key(0, 42)); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, vec![69], vec![69], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, vec![69], false)); assert_eq!(Balances::reserved_balance(&1), 13); assert!(InstanceMetadataOf::::contains_key(0, 69)); @@ -199,9 +199,9 @@ fn transfer_owner_should_work() { assert_noop!(Uniques::transfer_ownership(Origin::signed(1), 0, 1), Error::::NoPermission); // Mint and set metadata now and make sure that deposit gets transferred back. - assert_ok!(Uniques::set_class_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(2), 0, vec![0u8; 20], false)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - assert_ok!(Uniques::set_metadata(Origin::signed(2), 0, 42, vec![0u8; 10], vec![0u8; 10], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(2), 0, 42, vec![0u8; 20], false)); assert_ok!(Uniques::transfer_ownership(Origin::signed(2), 0, 3)); assert_eq!(Balances::total_balance(&2), 57); assert_eq!(Balances::total_balance(&3), 145); @@ -229,57 +229,53 @@ fn set_class_metadata_should_work() { new_test_ext().execute_with(|| { // Cannot add metadata to unknown asset assert_noop!( - Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], false), + Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 20], false), Error::::Unknown, ); assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); // Cannot add metadata to unowned asset assert_noop!( - Uniques::set_class_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], false), + Uniques::set_class_metadata(Origin::signed(2), 0, vec![0u8; 20], false), Error::::NoPermission, ); // Cannot add oversized metadata assert_noop!( - Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 51], vec![0u8; 1], false), - Error::::BadMetadata, - ); - assert_noop!( - Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 1], vec![0u8; 51], false), + Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 51], false), Error::::BadMetadata, ); // Successfully add metadata and take deposit Balances::make_free_balance_be(&1, 30); - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 20], false)); assert_eq!(Balances::free_balance(&1), 9); assert!(ClassMetadataOf::::contains_key(0)); // Force origin works, too. - assert_ok!(Uniques::set_class_metadata(Origin::root(), 0, vec![0u8; 9], vec![0u8; 9], false)); + assert_ok!(Uniques::set_class_metadata(Origin::root(), 0, vec![0u8; 18], false)); // Update deposit - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 5], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 15], false)); assert_eq!(Balances::free_balance(&1), 14); - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 15], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 25], false)); assert_eq!(Balances::free_balance(&1), 4); // Cannot over-reserve assert_noop!( - Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 20], vec![0u8; 20], false), + Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 40], false), BalancesError::::InsufficientBalance, ); // Can't set or clear metadata once frozen - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 5], true)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 15], true)); assert_noop!( - Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 5], false), + Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 15], false), Error::::Frozen, ); assert_noop!(Uniques::clear_class_metadata(Origin::signed(1), 0), Error::::Frozen); // Clear Metadata - assert_ok!(Uniques::set_class_metadata(Origin::root(), 0, vec![0u8; 10], vec![0u8; 5], false)); + assert_ok!(Uniques::set_class_metadata(Origin::root(), 0, vec![0u8; 15], false)); assert_noop!(Uniques::clear_class_metadata(Origin::signed(2), 0), Error::::NoPermission); assert_noop!(Uniques::clear_class_metadata(Origin::signed(1), 1), Error::::Unknown); assert_ok!(Uniques::clear_class_metadata(Origin::signed(1), 0)); @@ -297,50 +293,46 @@ fn set_instance_metadata_should_work() { assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); // Cannot add metadata to unowned asset assert_noop!( - Uniques::set_metadata(Origin::signed(2), 0, 42, vec![0u8; 10], vec![0u8; 10], false), + Uniques::set_metadata(Origin::signed(2), 0, 42, vec![0u8; 20], false), Error::::NoPermission, ); // Cannot add oversized metadata assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 51], vec![0u8; 1], false), - Error::::BadMetadata, - ); - assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 1], vec![0u8; 51], false), + Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 51], false), Error::::BadMetadata, ); // Successfully add metadata and take deposit - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 10], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 20], false)); assert_eq!(Balances::free_balance(&1), 8); assert!(InstanceMetadataOf::::contains_key(0, 42)); // Force origin works, too. - assert_ok!(Uniques::set_metadata(Origin::root(), 0, 42, vec![0u8; 9], vec![0u8; 9], false)); + assert_ok!(Uniques::set_metadata(Origin::root(), 0, 42, vec![0u8; 18], false)); // Update deposit - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 5], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 15], false)); assert_eq!(Balances::free_balance(&1), 13); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 15], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 25], false)); assert_eq!(Balances::free_balance(&1), 3); // Cannot over-reserve assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 20], vec![0u8; 20], false), + Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 40], false), BalancesError::::InsufficientBalance, ); // Can't set or clear metadata once frozen - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 5], true)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 15], true)); assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 10], vec![0u8; 5], false), + Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 15], false), Error::::Frozen, ); assert_noop!(Uniques::clear_metadata(Origin::signed(1), 0, 42), Error::::Frozen); // Clear Metadata - assert_ok!(Uniques::set_metadata(Origin::root(), 0, 42, vec![0u8; 10], vec![0u8; 5], false)); + assert_ok!(Uniques::set_metadata(Origin::root(), 0, 42, vec![0u8; 15], false)); assert_noop!(Uniques::clear_metadata(Origin::signed(2), 0, 42), Error::::NoPermission); assert_noop!(Uniques::clear_metadata(Origin::signed(1), 1, 42), Error::::Unknown); assert_ok!(Uniques::clear_metadata(Origin::signed(1), 0, 42)); diff --git a/frame/uniques/src/types.rs b/frame/uniques/src/types.rs index ccdb0faa62ba1..3ba82897dbfc5 100644 --- a/frame/uniques/src/types.rs +++ b/frame/uniques/src/types.rs @@ -94,12 +94,10 @@ pub struct ClassMetadata { /// /// This pays for the data stored in this struct. pub(super) deposit: DepositBalance, - /// The user friendly name of this asset. Limited in length by `StringLimit`. - pub(super) name: Vec, /// General information concerning this asset. Limited in length by `StringLimit`. This will /// generally be either a JSON dump or the hash of some JSON which can be found on a /// hash-addressable global publication system such as IPFS. - pub(super) information: Vec, + pub(super) data: Vec, /// Whether the asset metadata may be changed by a non Force origin. pub(super) is_frozen: bool, } @@ -110,12 +108,10 @@ pub struct InstanceMetadata { /// /// This pays for the data stored in this struct. pub(super) deposit: DepositBalance, - /// The user friendly name of this asset. Limited in length by `StringLimit`. - pub(super) name: Vec, /// General information concerning this asset. Limited in length by `StringLimit`. This will /// generally be either a JSON dump or the hash of some JSON which can be found on a /// hash-addressable global publication system such as IPFS. - pub(super) information: Vec, + pub(super) data: Vec, /// Whether the asset metadata may be changed by a non Force origin. pub(super) is_frozen: bool, } From 092127c4466a53587381edc3e8f5d6df0bb5ce2a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 24 May 2021 12:09:59 +0100 Subject: [PATCH 42/54] Use BoundedVec --- frame/support/src/storage/bounded_vec.rs | 6 + frame/uniques/src/benchmarking.rs | 22 ++-- frame/uniques/src/lib.rs | 45 ++++---- frame/uniques/src/tests.rs | 139 +++++++++++------------ frame/uniques/src/types.rs | 9 +- 5 files changed, 110 insertions(+), 111 deletions(-) diff --git a/frame/support/src/storage/bounded_vec.rs b/frame/support/src/storage/bounded_vec.rs index 6bb6ea541c331..9b111f7034d45 100644 --- a/frame/support/src/storage/bounded_vec.rs +++ b/frame/support/src/storage/bounded_vec.rs @@ -80,6 +80,12 @@ impl BoundedVec { } } +impl> From> for Vec { + fn from(x: BoundedVec) -> Vec { + x.0 + } +} + impl> BoundedVec { /// Get the bound of the type in `usize`. pub fn bound() -> usize { diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index 11870dbf32adc..ca6d656bd5005 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -19,14 +19,14 @@ #![cfg(feature = "runtime-benchmarks")] -use sp_std::prelude::*; +use sp_std::{prelude::*, convert::TryInto}; use super::*; use sp_runtime::traits::Bounded; use frame_system::RawOrigin as SystemOrigin; use frame_benchmarking::{ benchmarks_instance_pallet, account, whitelisted_caller, whitelist_account, impl_benchmark_test_suite }; -use frame_support::{traits::{Get, EnsureOrigin}, dispatch::UnfilteredDispatchable}; +use frame_support::{traits::{Get, EnsureOrigin}, dispatch::UnfilteredDispatchable, BoundedVec}; use crate::Pallet as Uniques; @@ -58,7 +58,7 @@ fn add_class_metadata, I: 'static>() assert!(Uniques::::set_class_metadata( SystemOrigin::Signed(caller.clone()).into(), Default::default(), - vec![0; T::StringLimit::get() as usize], + vec![0; T::StringLimit::get() as usize].try_into().unwrap(), false, ).is_ok()); (caller, caller_lookup) @@ -94,27 +94,27 @@ fn add_instance_metadata, I: 'static>(instance: T::InstanceId) SystemOrigin::Signed(caller.clone()).into(), Default::default(), instance, - vec![0; T::StringLimit::get() as usize], + vec![0; T::StringLimit::get() as usize].try_into().unwrap(), false, ).is_ok()); (caller, caller_lookup) } fn add_instance_attribute, I: 'static>(instance: T::InstanceId) - -> (Vec, T::AccountId, ::Source) + -> (BoundedVec, T::AccountId, ::Source) { let caller = Class::::get(T::ClassId::default()).unwrap().owner; if caller != whitelisted_caller() { whitelist_account!(caller); } let caller_lookup = T::Lookup::unlookup(caller.clone()); - let key = vec![0; T::KeyLimit::get() as usize]; + let key: BoundedVec<_, _> = vec![0; T::KeyLimit::get() as usize].try_into().unwrap(); assert!(Uniques::::set_attribute( SystemOrigin::Signed(caller.clone()).into(), Default::default(), Some(instance), key.clone(), - vec![0; T::ValueLimit::get() as usize], + vec![0; T::ValueLimit::get() as usize].try_into().unwrap(), ).is_ok()); (key, caller, caller_lookup) } @@ -293,8 +293,8 @@ benchmarks_instance_pallet! { } set_attribute { - let key = vec![0u8; T::KeyLimit::get() as usize]; - let value = vec![0u8; T::ValueLimit::get() as usize]; + let key: BoundedVec<_, _> = vec![0u8; T::KeyLimit::get() as usize].try_into().unwrap(); + let value: BoundedVec<_, _> = vec![0u8; T::ValueLimit::get() as usize].try_into().unwrap(); let (class, caller, _) = create_class::(); let (instance, ..) = mint_instance::(0); @@ -315,7 +315,7 @@ benchmarks_instance_pallet! { } set_metadata { - let data = vec![0u8; T::StringLimit::get() as usize]; + let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap(); let (class, caller, _) = create_class::(); let (instance, ..) = mint_instance::(0); @@ -334,7 +334,7 @@ benchmarks_instance_pallet! { } set_class_metadata { - let data = vec![0u8; T::StringLimit::get() as usize]; + let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap(); let (class, caller, _) = create_class::(); }: _(SystemOrigin::Signed(caller), class, data.clone(), false) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 7cf55e1f70a12..5bf34ce48cbbd 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -146,7 +146,7 @@ pub mod pallet { _, Blake2_128Concat, T::ClassId, - ClassMetadata>, + ClassMetadata, T::StringLimit>, OptionQuery, >; @@ -158,7 +158,7 @@ pub mod pallet { T::ClassId, Blake2_128Concat, T::InstanceId, - InstanceMetadata>, + InstanceMetadata, T::StringLimit>, OptionQuery, >; @@ -169,9 +169,9 @@ pub mod pallet { ( NMapKey, NMapKey>, - NMapKey>, + NMapKey>, ), - (Vec, DepositBalanceOf), + (BoundedVec, DepositBalanceOf), OptionQuery >; @@ -219,22 +219,27 @@ pub mod pallet { /// \[ class \] AssetStatusChanged(T::ClassId), /// New metadata has been set for an asset class. \[ class, data, is_frozen \] - ClassMetadataSet(T::ClassId, Vec, bool), + ClassMetadataSet(T::ClassId, BoundedVec, bool), /// Metadata has been cleared for an asset class. \[ class \] ClassMetadataCleared(T::ClassId), /// New metadata has been set for an asset instance. /// \[ class, instance, data, is_frozen \] - MetadataSet(T::ClassId, T::InstanceId, Vec, bool), + MetadataSet(T::ClassId, T::InstanceId, BoundedVec, bool), /// Metadata has been cleared for an asset instance. \[ class, instance \] MetadataCleared(T::ClassId, T::InstanceId), /// Metadata has been cleared for an asset instance. \[ class, successful_instances \] Redeposited(T::ClassId, Vec), /// New attribute metadata has been set for an asset class or instance. /// \[ class, maybe_instance, key, value \] - AttributeSet(T::ClassId, Option, Vec, Vec), + AttributeSet( + T::ClassId, + Option, + BoundedVec, + BoundedVec, + ), /// Attribute metadata has been cleared for an asset class or instance. /// \[ class, maybe_instance, key, maybe_value \] - AttributeCleared(T::ClassId, Option, Vec), + AttributeCleared(T::ClassId, Option, BoundedVec), } #[pallet::error] @@ -259,8 +264,6 @@ pub mod pallet { NoDelegate, /// No approval exists that would allow the transfer. Unapproved, - /// Invalid metadata given. - BadMetadata, } #[pallet::hooks] @@ -974,16 +977,13 @@ pub mod pallet { origin: OriginFor, #[pallet::compact] class: T::ClassId, maybe_instance: Option, - key: Vec, - value: Vec, + key: BoundedVec, + value: BoundedVec, ) -> DispatchResult { let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - ensure!(key.len() <= T::KeyLimit::get() as usize, Error::::BadMetadata); - ensure!(value.len() <= T::ValueLimit::get() as usize, Error::::BadMetadata); - let mut class_details = Class::::get(&class).ok_or(Error::::Unknown)?; if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &class_details.owner, Error::::NoPermission); @@ -1042,13 +1042,12 @@ pub mod pallet { origin: OriginFor, #[pallet::compact] class: T::ClassId, maybe_instance: Option, - key: Vec, + key: BoundedVec, ) -> DispatchResult { let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - ensure!(key.len() <= T::KeyLimit::get() as usize, Error::::BadMetadata); let mut class_details = Class::::get(&class).ok_or(Error::::Unknown)?; if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &class_details.owner, Error::::NoPermission); @@ -1092,15 +1091,13 @@ pub mod pallet { origin: OriginFor, #[pallet::compact] class: T::ClassId, #[pallet::compact] instance: T::InstanceId, - data: Vec, + data: BoundedVec, is_frozen: bool, ) -> DispatchResult { let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - ensure!(data.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); - let mut class_details = Class::::get(&class) .ok_or(Error::::Unknown)?; @@ -1120,7 +1117,7 @@ pub mod pallet { let mut deposit = Zero::zero(); if !class_details.free_holding && maybe_check_owner.is_some() { deposit = T::DepositPerByte::get() - .saturating_mul(((name.len() + info.len()) as u32).into()) + .saturating_mul(((data.len()) as u32).into()) .saturating_add(T::MetadataDepositBase::get()); } if deposit > old_deposit { @@ -1208,15 +1205,13 @@ pub mod pallet { pub(super) fn set_class_metadata( origin: OriginFor, #[pallet::compact] class: T::ClassId, - data: Vec, + data: BoundedVec, is_frozen: bool, ) -> DispatchResult { let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - ensure!(data.len() <= T::StringLimit::get() as usize, Error::::BadMetadata); - let mut details = Class::::get(&class).ok_or(Error::::Unknown)?; if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &details.owner, Error::::NoPermission); @@ -1231,7 +1226,7 @@ pub mod pallet { let mut deposit = Zero::zero(); if maybe_check_owner.is_some() && !details.free_holding { deposit = T::DepositPerByte::get() - .saturating_mul(((name.len() + info.len()) as u32).into()) + .saturating_mul(((data.len()) as u32).into()) .saturating_add(T::MetadataDepositBase::get()); } if deposit > old_deposit { diff --git a/frame/uniques/src/tests.rs b/frame/uniques/src/tests.rs index a8fe8fe5961c8..4673ff71f8ed9 100644 --- a/frame/uniques/src/tests.rs +++ b/frame/uniques/src/tests.rs @@ -19,6 +19,7 @@ use super::*; use crate::mock::*; +use sp_std::convert::TryInto; use frame_support::{assert_ok, assert_noop, traits::Currency}; use pallet_balances::Error as BalancesError; @@ -45,8 +46,16 @@ fn assets() -> Vec<(u64, u32, u32)> { r } +macro_rules! bvec { + ($( $x:tt )*) => { + vec![$( $x )*].try_into().unwrap() + } +} + fn attributes(class: u32) -> Vec<(Option, Vec, Vec)> { - let mut s: Vec<_> = Attribute::::iter_prefix((class,)).map(|(k, v)| (k.0, k.1, v.0)).collect(); + let mut s: Vec<_> = Attribute::::iter_prefix((class,)) + .map(|(k, v)| (k.0, k.1.into(), v.0.into())) + .collect(); s.sort(); s } @@ -78,7 +87,7 @@ fn lifecycle_should_work() { assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_eq!(Balances::reserved_balance(&1), 2); - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, bvec![0, 0], false)); assert_eq!(Balances::reserved_balance(&1), 5); assert!(ClassMetadataOf::::contains_key(0)); @@ -90,10 +99,10 @@ fn lifecycle_should_work() { assert_eq!(Class::::get(0).unwrap().instances, 2); assert_eq!(Class::::get(0).unwrap().instance_metadatas, 0); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![42], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, bvec![42, 42], false)); assert_eq!(Balances::reserved_balance(&1), 10); assert!(InstanceMetadataOf::::contains_key(0, 42)); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, vec![69], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, bvec![69, 69], false)); assert_eq!(Balances::reserved_balance(&1), 13); assert!(InstanceMetadataOf::::contains_key(0, 69)); @@ -199,9 +208,9 @@ fn transfer_owner_should_work() { assert_noop!(Uniques::transfer_ownership(Origin::signed(1), 0, 1), Error::::NoPermission); // Mint and set metadata now and make sure that deposit gets transferred back. - assert_ok!(Uniques::set_class_metadata(Origin::signed(2), 0, vec![0u8; 20], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(2), 0, bvec![0u8; 20], false)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); - assert_ok!(Uniques::set_metadata(Origin::signed(2), 0, 42, vec![0u8; 20], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(2), 0, 42, bvec![0u8; 20], false)); assert_ok!(Uniques::transfer_ownership(Origin::signed(2), 0, 3)); assert_eq!(Balances::total_balance(&2), 57); assert_eq!(Balances::total_balance(&3), 145); @@ -229,53 +238,47 @@ fn set_class_metadata_should_work() { new_test_ext().execute_with(|| { // Cannot add metadata to unknown asset assert_noop!( - Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 20], false), + Uniques::set_class_metadata(Origin::signed(1), 0, bvec![0u8; 20], false), Error::::Unknown, ); assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); // Cannot add metadata to unowned asset assert_noop!( - Uniques::set_class_metadata(Origin::signed(2), 0, vec![0u8; 20], false), + Uniques::set_class_metadata(Origin::signed(2), 0, bvec![0u8; 20], false), Error::::NoPermission, ); - // Cannot add oversized metadata - assert_noop!( - Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 51], false), - Error::::BadMetadata, - ); - // Successfully add metadata and take deposit Balances::make_free_balance_be(&1, 30); - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 20], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, bvec![0u8; 20], false)); assert_eq!(Balances::free_balance(&1), 9); assert!(ClassMetadataOf::::contains_key(0)); // Force origin works, too. - assert_ok!(Uniques::set_class_metadata(Origin::root(), 0, vec![0u8; 18], false)); + assert_ok!(Uniques::set_class_metadata(Origin::root(), 0, bvec![0u8; 18], false)); // Update deposit - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 15], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, bvec![0u8; 15], false)); assert_eq!(Balances::free_balance(&1), 14); - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 25], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, bvec![0u8; 25], false)); assert_eq!(Balances::free_balance(&1), 4); // Cannot over-reserve assert_noop!( - Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 40], false), + Uniques::set_class_metadata(Origin::signed(1), 0, bvec![0u8; 40], false), BalancesError::::InsufficientBalance, ); // Can't set or clear metadata once frozen - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 15], true)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, bvec![0u8; 15], true)); assert_noop!( - Uniques::set_class_metadata(Origin::signed(1), 0, vec![0u8; 15], false), + Uniques::set_class_metadata(Origin::signed(1), 0, bvec![0u8; 15], false), Error::::Frozen, ); assert_noop!(Uniques::clear_class_metadata(Origin::signed(1), 0), Error::::Frozen); // Clear Metadata - assert_ok!(Uniques::set_class_metadata(Origin::root(), 0, vec![0u8; 15], false)); + assert_ok!(Uniques::set_class_metadata(Origin::root(), 0, bvec![0u8; 15], false)); assert_noop!(Uniques::clear_class_metadata(Origin::signed(2), 0), Error::::NoPermission); assert_noop!(Uniques::clear_class_metadata(Origin::signed(1), 1), Error::::Unknown); assert_ok!(Uniques::clear_class_metadata(Origin::signed(1), 0)); @@ -293,46 +296,40 @@ fn set_instance_metadata_should_work() { assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); // Cannot add metadata to unowned asset assert_noop!( - Uniques::set_metadata(Origin::signed(2), 0, 42, vec![0u8; 20], false), + Uniques::set_metadata(Origin::signed(2), 0, 42, bvec![0u8; 20], false), Error::::NoPermission, ); - // Cannot add oversized metadata - assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 51], false), - Error::::BadMetadata, - ); - // Successfully add metadata and take deposit - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 20], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, bvec![0u8; 20], false)); assert_eq!(Balances::free_balance(&1), 8); assert!(InstanceMetadataOf::::contains_key(0, 42)); // Force origin works, too. - assert_ok!(Uniques::set_metadata(Origin::root(), 0, 42, vec![0u8; 18], false)); + assert_ok!(Uniques::set_metadata(Origin::root(), 0, 42, bvec![0u8; 18], false)); // Update deposit - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 15], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, bvec![0u8; 15], false)); assert_eq!(Balances::free_balance(&1), 13); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 25], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, bvec![0u8; 25], false)); assert_eq!(Balances::free_balance(&1), 3); // Cannot over-reserve assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 40], false), + Uniques::set_metadata(Origin::signed(1), 0, 42, bvec![0u8; 40], false), BalancesError::::InsufficientBalance, ); // Can't set or clear metadata once frozen - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 15], true)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, bvec![0u8; 15], true)); assert_noop!( - Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0u8; 15], false), + Uniques::set_metadata(Origin::signed(1), 0, 42, bvec![0u8; 15], false), Error::::Frozen, ); assert_noop!(Uniques::clear_metadata(Origin::signed(1), 0, 42), Error::::Frozen); // Clear Metadata - assert_ok!(Uniques::set_metadata(Origin::root(), 0, 42, vec![0u8; 15], false)); + assert_ok!(Uniques::set_metadata(Origin::root(), 0, 42, bvec![0u8; 15], false)); assert_noop!(Uniques::clear_metadata(Origin::signed(2), 0, 42), Error::::NoPermission); assert_noop!(Uniques::clear_metadata(Origin::signed(1), 1, 42), Error::::Unknown); assert_ok!(Uniques::clear_metadata(Origin::signed(1), 0, 42)); @@ -347,28 +344,28 @@ fn set_attribute_should_work() { assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); - assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, None, vec![0], vec![0])); - assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), vec![0], vec![0])); - assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), vec![1], vec![0])); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, None, bvec![0], bvec![0])); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), bvec![0], bvec![0])); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), bvec![1], bvec![0])); assert_eq!(attributes(0), vec![ - (None, vec![0], vec![0]), - (Some(0), vec![0], vec![0]), - (Some(0), vec![1], vec![0]), + (None, bvec![0], bvec![0]), + (Some(0), bvec![0], bvec![0]), + (Some(0), bvec![1], bvec![0]), ]); assert_eq!(Balances::reserved_balance(1), 9); - assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, None, vec![0], vec![0; 10])); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, None, bvec![0], bvec![0; 10])); assert_eq!(attributes(0), vec![ - (None, vec![0], vec![0; 10]), - (Some(0), vec![0], vec![0]), - (Some(0), vec![1], vec![0]), + (None, bvec![0], bvec![0; 10]), + (Some(0), bvec![0], bvec![0]), + (Some(0), bvec![1], bvec![0]), ]); assert_eq!(Balances::reserved_balance(1), 18); - assert_ok!(Uniques::clear_attribute(Origin::signed(1), 0, Some(0), vec![1])); + assert_ok!(Uniques::clear_attribute(Origin::signed(1), 0, Some(0), bvec![1])); assert_eq!(attributes(0), vec![ - (None, vec![0], vec![0; 10]), - (Some(0), vec![0], vec![0]), + (None, bvec![0], bvec![0; 10]), + (Some(0), bvec![0], bvec![0]), ]); assert_eq!(Balances::reserved_balance(1), 15); @@ -386,25 +383,25 @@ fn set_attribute_should_respect_freeze() { assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); - assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, None, vec![0], vec![0])); - assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), vec![0], vec![0])); - assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(1), vec![0], vec![0])); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, None, bvec![0], bvec![0])); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), bvec![0], bvec![0])); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(1), bvec![0], bvec![0])); assert_eq!(attributes(0), vec![ - (None, vec![0], vec![0]), - (Some(0), vec![0], vec![0]), - (Some(1), vec![0], vec![0]), + (None, bvec![0], bvec![0]), + (Some(0), bvec![0], bvec![0]), + (Some(1), bvec![0], bvec![0]), ]); assert_eq!(Balances::reserved_balance(1), 9); - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![], vec![], true)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, bvec![], true)); let e = Error::::Frozen; - assert_noop!(Uniques::set_attribute(Origin::signed(1), 0, None, vec![0], vec![0]), e); - assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), vec![0], vec![1])); + assert_noop!(Uniques::set_attribute(Origin::signed(1), 0, None, bvec![0], bvec![0]), e); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), bvec![0], bvec![1])); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 0, vec![], vec![], true)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 0, bvec![], true)); let e = Error::::Frozen; - assert_noop!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), vec![0], vec![1]), e); - assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(1), vec![0], vec![1])); + assert_noop!(Uniques::set_attribute(Origin::signed(1), 0, Some(0), bvec![0], bvec![1]), e); + assert_ok!(Uniques::set_attribute(Origin::signed(1), 0, Some(1), bvec![0], bvec![1])); }); } @@ -416,29 +413,29 @@ fn force_asset_status_should_work(){ assert_ok!(Uniques::force_create(Origin::root(), 0, 1, false)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 42, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 69, 2)); - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0; 10], vec![0; 10], false)); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0; 10], vec![0; 10], false)); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, vec![0; 10], vec![0; 10], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, bvec![0; 20], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, bvec![0; 20], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, bvec![0; 20], false)); assert_eq!(Balances::reserved_balance(1), 65); //force asset status to be free holding assert_ok!(Uniques::force_asset_status(Origin::root(), 0, 1, 1, 1, 1, true, false)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 142, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 169, 2)); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 142, vec![0; 10], vec![0; 10], false)); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 169, vec![0; 10], vec![0; 10], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 142, bvec![0; 20], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 169, bvec![0; 20], false)); assert_eq!(Balances::reserved_balance(1), 65); - assert_ok!(Uniques::redeposit(Origin::signed(1), 0, vec![0, 42, 50, 69, 100])); + assert_ok!(Uniques::redeposit(Origin::signed(1), 0, bvec![0, 42, 50, 69, 100])); assert_eq!(Balances::reserved_balance(1), 63); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, vec![0; 10], vec![0; 10], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 42, bvec![0; 20], false)); assert_eq!(Balances::reserved_balance(1), 42); - assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, vec![0; 10], vec![0; 10], false)); + assert_ok!(Uniques::set_metadata(Origin::signed(1), 0, 69, bvec![0; 20], false)); assert_eq!(Balances::reserved_balance(1), 21); - assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, vec![0; 10], vec![0; 10], false)); + assert_ok!(Uniques::set_class_metadata(Origin::signed(1), 0, bvec![0; 20], false)); assert_eq!(Balances::reserved_balance(1), 0); }); } diff --git a/frame/uniques/src/types.rs b/frame/uniques/src/types.rs index 3ba82897dbfc5..45b571aa7de2c 100644 --- a/frame/uniques/src/types.rs +++ b/frame/uniques/src/types.rs @@ -18,6 +18,7 @@ //! Various basic types for use in the assets pallet. use super::*; +use frame_support::{traits::Get, BoundedVec}; pub(super) type DepositBalanceOf = <>::Currency as Currency<::AccountId>>::Balance; @@ -89,7 +90,7 @@ pub struct InstanceDetails { } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default)] -pub struct ClassMetadata { +pub struct ClassMetadata> { /// The balance deposited for this metadata. /// /// This pays for the data stored in this struct. @@ -97,13 +98,13 @@ pub struct ClassMetadata { /// General information concerning this asset. Limited in length by `StringLimit`. This will /// generally be either a JSON dump or the hash of some JSON which can be found on a /// hash-addressable global publication system such as IPFS. - pub(super) data: Vec, + pub(super) data: BoundedVec, /// Whether the asset metadata may be changed by a non Force origin. pub(super) is_frozen: bool, } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default)] -pub struct InstanceMetadata { +pub struct InstanceMetadata> { /// The balance deposited for this metadata. /// /// This pays for the data stored in this struct. @@ -111,7 +112,7 @@ pub struct InstanceMetadata { /// General information concerning this asset. Limited in length by `StringLimit`. This will /// generally be either a JSON dump or the hash of some JSON which can be found on a /// hash-addressable global publication system such as IPFS. - pub(super) data: Vec, + pub(super) data: BoundedVec, /// Whether the asset metadata may be changed by a non Force origin. pub(super) is_frozen: bool, } From f9f6f5190875e86d7c18a3c49362669e82bad245 Mon Sep 17 00:00:00 2001 From: Parity Bot Date: Mon, 24 May 2021 11:35:51 +0000 Subject: [PATCH 43/54] cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_uniques --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/uniques/src/weights.rs --template=./.maintain/frame-weight-template.hbs --- frame/uniques/src/weights.rs | 250 ++++++++++++++++++----------------- 1 file changed, 127 insertions(+), 123 deletions(-) diff --git a/frame/uniques/src/weights.rs b/frame/uniques/src/weights.rs index 625a60d061dbc..9272ae6026a9f 100644 --- a/frame/uniques/src/weights.rs +++ b/frame/uniques/src/weights.rs @@ -15,10 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_assets +//! Autogenerated weights for pallet_uniques //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-03-08, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-05-24, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -27,12 +27,12 @@ // --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_assets +// --pallet=pallet_uniques // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/assets/src/weights.rs +// --output=./frame/uniques/src/weights.rs // --template=./.maintain/frame-weight-template.hbs @@ -42,11 +42,11 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use sp_std::marker::PhantomData; -/// Weight functions needed for pallet_assets. +/// Weight functions needed for pallet_uniques. pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; - fn destroy(n: u32, m: u32, _a: u32, ) -> Weight; + fn destroy(n: u32, m: u32, a: u32, ) -> Weight; fn mint() -> Weight; fn burn() -> Weight; fn transfer() -> Weight; @@ -57,8 +57,6 @@ pub trait WeightInfo { fn thaw_class() -> Weight; fn transfer_ownership() -> Weight; fn set_team() -> Weight; - fn approve_transfer() -> Weight; - fn cancel_approval() -> Weight; fn force_asset_status() -> Weight; fn set_attribute() -> Weight; fn clear_attribute() -> Weight; @@ -66,130 +64,134 @@ pub trait WeightInfo { fn clear_metadata() -> Weight; fn set_class_metadata() -> Weight; fn clear_class_metadata() -> Weight; + fn approve_transfer() -> Weight; + fn cancel_approval() -> Weight; } -/// Weights for pallet_assets using the Substrate node and recommended hardware. +/// Weights for pallet_uniques using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn create() -> Weight { - (48_305_000 as Weight) + (55_264_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_create() -> Weight { - (23_827_000 as Weight) + (28_173_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn destroy(n: u32, m: u32, _a: u32, ) -> Weight { + fn destroy(n: u32, m: u32, a: u32, ) -> Weight { (0 as Weight) - // Standard Error: 38_000 - .saturating_add((24_232_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 38_000 - .saturating_add((30_467_000 as Weight).saturating_mul(m as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(n as Weight))) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(m as Weight))) + // Standard Error: 32_000 + .saturating_add((23_077_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 32_000 + .saturating_add((1_723_000 as Weight).saturating_mul(m as Weight)) + // Standard Error: 32_000 + .saturating_add((1_534_000 as Weight).saturating_mul(a as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(n as Weight))) - .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(m as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(m as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(a as Weight))) } fn mint() -> Weight { - (46_433_000 as Weight) + (73_250_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn burn() -> Weight { - (46_000_000 as Weight) + (74_443_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn transfer() -> Weight { - (70_793_000 as Weight) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) + (54_690_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn redeposit(_i: u32, ) -> Weight { - (70_968_000 as Weight) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) + fn redeposit(i: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 19_000 + .saturating_add((34_624_000 as Weight).saturating_mul(i as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(i as Weight))) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight))) } fn freeze() -> Weight { - (34_290_000 as Weight) + (39_505_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn thaw() -> Weight { - (34_419_000 as Weight) + (38_844_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn freeze_class() -> Weight { - (24_373_000 as Weight) + (28_739_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn thaw_class() -> Weight { - (24_096_000 as Weight) + (28_963_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn transfer_ownership() -> Weight { - (28_566_000 as Weight) + (65_160_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn set_team() -> Weight { - (25_297_000 as Weight) + (30_000_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn approve_transfer() -> Weight { - (47_906_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - fn cancel_approval() -> Weight { - (48_591_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } fn force_asset_status() -> Weight { - (23_366_000 as Weight) + (29_145_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_attribute() -> Weight { - (27_117_000 as Weight) - // Standard Error: 0 - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (88_923_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn clear_attribute() -> Weight { - (27_117_000 as Weight) - // Standard Error: 0 - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (79_878_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn set_metadata() -> Weight { - (27_117_000 as Weight) - // Standard Error: 0 + (67_110_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn clear_metadata() -> Weight { - (51_598_000 as Weight) + (66_191_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn set_class_metadata() -> Weight { - (53_367_000 as Weight) - // Standard Error: 0 + (65_558_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn clear_class_metadata() -> Weight { - (51_721_000 as Weight) + (60_135_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn approve_transfer() -> Weight { + (40_337_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn cancel_approval() -> Weight { + (40_770_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -198,124 +200,126 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn create() -> Weight { - (48_305_000 as Weight) + (55_264_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_create() -> Weight { - (23_827_000 as Weight) + (28_173_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn destroy(n: u32, m: u32, _a: u32, ) -> Weight { + fn destroy(n: u32, m: u32, a: u32, ) -> Weight { (0 as Weight) - // Standard Error: 38_000 - .saturating_add((24_232_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 38_000 - .saturating_add((30_467_000 as Weight).saturating_mul(m as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(n as Weight))) - .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(m as Weight))) + // Standard Error: 32_000 + .saturating_add((23_077_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 32_000 + .saturating_add((1_723_000 as Weight).saturating_mul(m as Weight)) + // Standard Error: 32_000 + .saturating_add((1_534_000 as Weight).saturating_mul(a as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) .saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(n as Weight))) - .saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(m as Weight))) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(m as Weight))) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(a as Weight))) } fn mint() -> Weight { - (46_433_000 as Weight) + (73_250_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn burn() -> Weight { - (46_000_000 as Weight) + (74_443_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn transfer() -> Weight { - (70_793_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + (54_690_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn redeposit(_i: u32, ) -> Weight { - (70_968_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + fn redeposit(i: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 19_000 + .saturating_add((34_624_000 as Weight).saturating_mul(i as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(i as Weight))) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(i as Weight))) } fn freeze() -> Weight { - (34_290_000 as Weight) + (39_505_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn thaw() -> Weight { - (34_419_000 as Weight) + (38_844_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn freeze_class() -> Weight { - (24_373_000 as Weight) + (28_739_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn thaw_class() -> Weight { - (24_096_000 as Weight) + (28_963_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn transfer_ownership() -> Weight { - (28_566_000 as Weight) + (65_160_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn set_team() -> Weight { - (25_297_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - fn approve_transfer() -> Weight { - (47_906_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - fn cancel_approval() -> Weight { - (48_591_000 as Weight) + (30_000_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_asset_status() -> Weight { - (23_366_000 as Weight) + (29_145_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_attribute() -> Weight { - (27_117_000 as Weight) - // Standard Error: 0 - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (88_923_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn clear_attribute() -> Weight { - (27_117_000 as Weight) - // Standard Error: 0 - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (79_878_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn set_metadata() -> Weight { - (27_117_000 as Weight) - // Standard Error: 0 + (67_110_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn clear_metadata() -> Weight { - (51_598_000 as Weight) + (66_191_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn set_class_metadata() -> Weight { - (53_367_000 as Weight) - // Standard Error: 0 + (65_558_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn clear_class_metadata() -> Weight { - (51_721_000 as Weight) + (60_135_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn approve_transfer() -> Weight { + (40_337_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn cancel_approval() -> Weight { + (40_770_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } From f38e9b9ea2c78e900916a551d00c45f19aed8740 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 31 May 2021 11:07:30 +0100 Subject: [PATCH 44/54] Update frame/uniques/src/lib.rs Co-authored-by: Lohann Paterno Coutinho Ferreira --- frame/uniques/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 5bf34ce48cbbd..f47e1f9823533 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -965,7 +965,7 @@ pub mod pallet { /// account any already reserved funds. /// /// - `class`: The identifier of the asset class whose instance's metadata to set. - /// - `instance`: The identifier of the asset instance whose metadata to set. + /// - `maybe_instance`: The identifier of the asset instance whose metadata to set. /// - `key`: The key of the attribute. /// - `value`: The value to which to set the attribute. /// From 04c900ad8e9dd8cce2969de06961b95acf5b3ea7 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 31 May 2021 11:07:38 +0100 Subject: [PATCH 45/54] Update frame/uniques/src/lib.rs Co-authored-by: Lohann Paterno Coutinho Ferreira --- frame/uniques/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index f47e1f9823533..2e9ce7fd758f8 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -1195,7 +1195,7 @@ pub mod pallet { /// account any already reserved funds. /// /// - `class`: The identifier of the asset whose metadata to update. - /// - `info`: The general information of this asset. Limited in length by `StringLimit`. + /// - `data`: The general information of this asset. Limited in length by `StringLimit`. /// - `is_frozen`: Whether the metadata should be frozen against further changes. /// /// Emits `ClassMetadataSet`. From d41c1b49115ca4124d0f8ed5e090859922d273f4 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 31 May 2021 11:07:44 +0100 Subject: [PATCH 46/54] Update frame/uniques/src/lib.rs Co-authored-by: Lohann Paterno Coutinho Ferreira --- frame/uniques/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 2e9ce7fd758f8..b4cd373b52a13 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -1191,7 +1191,7 @@ pub mod pallet { /// the asset `class`. /// /// If the origin is `Signed`, then funds of signer are reserved according to the formula: - /// `MetadataDepositBase + DepositPerByte * name.len` taking into + /// `MetadataDepositBase + DepositPerByte * data.len` taking into /// account any already reserved funds. /// /// - `class`: The identifier of the asset whose metadata to update. From 7353b3426e3575c99047a69498ffad3537a7f35a Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 31 May 2021 11:07:51 +0100 Subject: [PATCH 47/54] Update frame/uniques/src/lib.rs Co-authored-by: Lohann Paterno Coutinho Ferreira --- frame/uniques/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index b4cd373b52a13..34a7a49217d5c 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -1075,7 +1075,7 @@ pub mod pallet { /// asset `class`. /// /// If the origin is Signed, then funds of signer are reserved according to the formula: - /// `MetadataDepositBase + DepositPerByte * (name.len + info.len)` taking into + /// `MetadataDepositBase + DepositPerByte * data.len` taking into /// account any already reserved funds. /// /// - `class`: The identifier of the asset class whose instance's metadata to set. From f84688d6cdf98aa132abae8ab7ea64281a0cc1c0 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 31 May 2021 11:07:56 +0100 Subject: [PATCH 48/54] Update frame/uniques/src/lib.rs Co-authored-by: Lohann Paterno Coutinho Ferreira --- frame/uniques/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 34a7a49217d5c..21142a3a92ce0 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -92,7 +92,7 @@ pub mod pallet { /// either "normal" metadata or attribute metadata. type DepositPerByte: Get>; - /// The maximum length of a name or symbol stored on-chain. + /// The maximum length of data stored on-chain. type StringLimit: Get; /// The maximum length of an attribute key. From bd82d6e177e243891e6ca830682f2a68ef8b0623 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 31 May 2021 13:37:28 +0200 Subject: [PATCH 49/54] Fixes --- bin/node/runtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e6348728af72a..97893a6cedb30 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -114,8 +114,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 265, - impl_version: 1, + spec_version: 266, + impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, }; From ca0b97c7fa6fcb5e6ef0c19a30cec0425b701198 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 31 May 2021 22:33:49 +0100 Subject: [PATCH 50/54] Update frame/uniques/README.md Co-authored-by: Alexander Popiak --- frame/uniques/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/uniques/README.md b/frame/uniques/README.md index 9aa9df18ab2a6..7006b9f0b2c27 100644 --- a/frame/uniques/README.md +++ b/frame/uniques/README.md @@ -1,6 +1,6 @@ # Uniques Module -A simple, secure module for dealing with fungible assets. +A simple, secure module for dealing with non-fungible assets. ## Overview From 59dbd0f49a3c06ca7a57b2f8ac1a464ff242cb10 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 31 May 2021 22:34:19 +0100 Subject: [PATCH 51/54] Update frame/uniques/README.md Co-authored-by: Alexander Popiak --- frame/uniques/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/uniques/README.md b/frame/uniques/README.md index 7006b9f0b2c27..3b152287d017c 100644 --- a/frame/uniques/README.md +++ b/frame/uniques/README.md @@ -4,8 +4,7 @@ A simple, secure module for dealing with non-fungible assets. ## Overview -The Assets module provides functionality for asset management of fungible asset classes -with a fixed supply, including: +The Uniques module provides functionality for asset management of non-fungible asset classes, including: * Asset Issuance * Asset Transfer From 2e36b5c821fcb8089161bf977456d0531243d519 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 31 May 2021 22:34:27 +0100 Subject: [PATCH 52/54] Update frame/uniques/README.md Co-authored-by: Alexander Popiak --- frame/uniques/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/uniques/README.md b/frame/uniques/README.md index 3b152287d017c..36ef00eb4a870 100644 --- a/frame/uniques/README.md +++ b/frame/uniques/README.md @@ -10,9 +10,9 @@ The Uniques module provides functionality for asset management of non-fungible a * Asset Transfer * Asset Destruction -To use it in your runtime, you need to implement the assets [`assets::Trait`](https://docs.rs/pallet-assets/latest/pallet_assets/trait.Trait.html). +To use it in your runtime, you need to implement the assets [`uniques::Config`](https://docs.rs/pallet-uniques/latest/pallet_uniques/pallet/trait.Config.html). -The supported dispatchable functions are documented in the [`assets::Call`](https://docs.rs/pallet-assets/latest/pallet_assets/enum.Call.html) enum. +The supported dispatchable functions are documented in the [`uniques::Call`](https://docs.rs/pallet-uniques/latest/pallet_uniques/pallet/enum.Call.html) enum. ### Terminology From f50d089b6e040e50f12d9391db0140ba5aed4a24 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 1 Jun 2021 13:25:13 +0200 Subject: [PATCH 53/54] Docs --- frame/uniques/README.md | 130 ++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 84 deletions(-) diff --git a/frame/uniques/README.md b/frame/uniques/README.md index 36ef00eb4a870..b924e338452ff 100644 --- a/frame/uniques/README.md +++ b/frame/uniques/README.md @@ -16,101 +16,63 @@ The supported dispatchable functions are documented in the [`uniques::Call`](htt ### Terminology -* **Asset issuance:** The creation of a new asset, whose total supply will belong to the - account that issues the asset. -* **Asset transfer:** The action of transferring assets from one account to another. -* **Asset destruction:** The process of an account removing its entire holding of an asset. -* **Fungible asset:** An asset whose units are interchangeable. -* **Non-fungible asset:** An asset for which each unit has unique characteristics. +* **Asset issuance:** The creation of a new asset instance. +* **Asset transfer:** The action of transferring an asset instance from one account to another. +* **Asset burning:** The destruction of an asset instance. +* **Non-fungible asset:** An asset for which each unit has unique characteristics. There is exactly + one instance of such an asset in existance and there is exactly one owning account. ### Goals -The assets system in Substrate is designed to make the following possible: +The Uniques pallet in Substrate is designed to make the following possible: -* Issue a unique asset to its creator's account. -* Move assets between accounts. -* Remove an account's balance of an asset when requested by that account's owner and update - the asset's total supply. +* Allow accounts to permissionlessly create asset classes (collections of asset instances). +* Allow a named (permissioned) account to mint and burn unique assets within a class. +* Move asset instances between accounts permissionlessly. +* Allow a named (permissioned) account to freeze and unfreeze unique assets within a + class or the entire class. +* Allow the owner of an asset instance to delegate the ability to transfer the asset to some + named third-party. ## Interface -### Dispatchable Functions - -* `issue` - Issues the total supply of a new fungible asset to the account of the caller of the function. -* `transfer` - Transfers an `amount` of units of fungible asset `id` from the balance of -the function caller's account (`origin`) to a `target` account. -* `destroy` - Destroys the entire holding of a fungible asset `id` associated with the account -that called the function. - -Please refer to the [`Call`](https://docs.rs/pallet-assets/latest/pallet_assets/enum.Call.html) enum and its associated variants for documentation on each function. - -### Public Functions - - -* `balance` - Get the asset `id` balance of `who`. -* `total_supply` - Get the total supply of an asset `id`. - -Please refer to the [`Module`](https://docs.rs/pallet-assets/latest/pallet_assets/struct.Module.html) struct for details on publicly available functions. - -## Usage - -The following example shows how to use the Assets module in your runtime by exposing public functions to: - -* Issue a new fungible asset for a token distribution event (airdrop). -* Query the fungible asset holding balance of an account. -* Query the total supply of a fungible asset that has been issued. - -### Prerequisites - -Import the Assets module and types and derive your runtime's configuration traits from the Assets module trait. - -### Simple Code Snippet - -```rust -use pallet_assets as assets; -use frame_support::{decl_module, dispatch, ensure}; -use frame_system::ensure_signed; -use sp_runtime::ArithmeticError; - -pub trait Config: assets::Config { } - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - pub fn issue_token_airdrop(origin) -> dispatch::DispatchResult { - let sender = ensure_signed(origin).map_err(|e| e.as_str())?; - - const ACCOUNT_ALICE: u64 = 1; - const ACCOUNT_BOB: u64 = 2; - const COUNT_AIRDROP_RECIPIENTS: u64 = 2; - const TOKENS_FIXED_SUPPLY: u64 = 100; - - ensure!(!COUNT_AIRDROP_RECIPIENTS.is_zero(), ArithmeticError::DivisionByZero); - - let asset_id = Self::next_asset_id(); - - >::mutate(|asset_id| *asset_id += 1); - >::insert((asset_id, &ACCOUNT_ALICE), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); - >::insert((asset_id, &ACCOUNT_BOB), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); - >::insert(asset_id, TOKENS_FIXED_SUPPLY); - - Self::deposit_event(RawEvent::Issued(asset_id, sender, TOKENS_FIXED_SUPPLY)); - Ok(()) - } - } -} -``` - -## Assumptions - -Below are assumptions that must be held when using this module. If any of -them are violated, the behavior of this module is undefined. - -* The total count of assets should be less than - `Config::AssetId::max_value()`. +### Permissionless dispatchables +* `create`: Create a new asset class by placing a deposit. +* `transfer`: Transfer an asset instance to a new owner. +* `redeposit`: Update the deposit amount of an asset instance, potentially freeing funds. +* `approve_transfer`: Name a delegate who may authorise a transfer. +* `cancel_approval`: Revert the effects of a previous `approve_transfer`. + +### Permissioned dispatchables +* `destroy`: Destroy an asset class. +* `mint`: Mint a new asset instance within an asset class. +* `burn`: Burn an asset instance within an asset class. +* `freeze`: Prevent an individual asset from being transferred. +* `thaw`: Revert the effects of a previous `freeze`. +* `freeze_class`: Prevent all asset within a class from being transferred. +* `thaw_class`: Revert the effects of a previous `freeze_class`. +* `transfer_ownership`: Alter the owner of an asset class, moving all associated deposits. +* `set_team`: Alter the permissioned accounts of an asset class. + +### Metadata (permissioned) dispatchables +* `set_attribute`: Set a metadata attribute of an asset instance or class. +* `clear_attribute`: Remove a metadata attribute of an asset instance or class. +* `set_metadata`: Set general metadata of an asset instance. +* `clear_metadata`: Remove general metadata of an asset instance. +* `set_class_metadata`: Set general metadata of an asset class. +* `clear_class_metadata`: Remove general metadata of an asset class. + +### Force (i.e. governance) dispatchables +* `force_create`: Create a new asset class. +* `force_asset_status`: Alter the underlying characteristics of an asset class. + +Please refer to the [`Call`](https://docs.rs/pallet-assets/latest/pallet_assets/enum.Call.html) enum +and its associated variants for documentation on each function. ## Related Modules * [`System`](https://docs.rs/frame-system/latest/frame_system/) * [`Support`](https://docs.rs/frame-support/latest/frame_support/) +* [`Assets`](https://docs.rs/pallet-assets/latest/pallet_assetss/) License: Apache-2.0 From f240bc40c024c42ad5daff66b178307588dfdbd5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 1 Jun 2021 13:25:54 +0200 Subject: [PATCH 54/54] Bump --- bin/node/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 97893a6cedb30..16a79ab2572fb 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -114,7 +114,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 266, + spec_version: 267, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2,