* Copy Uniques into Nfts

* Connect new pallet

* Update weights

* Nfts: Multiple approvals (#12178)

* multiple approvals

* clear

* tests & clean up

* fix in logic & fmt

* fix benchmarks

* deadline

* test deadline

* current_block + deadline

* update ApprovedTransfer event

* benchmark

* docs

* Update frame/nfts/src/lib.rs

Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>

* fmt fix

* Update frame/nfts/src/lib.rs

Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>

* update tests

* anyone can cancel

* Update frame/nfts/src/tests.rs

Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>

* fmt

* fix logic

* unnecessary line

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

* Update frame/nfts/src/lib.rs

* Update lib.rs

* fmt

* Update frame/nfts/src/lib.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* fmt

* Update frame/nfts/src/lib.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* suggestion

* new line

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>
Co-authored-by: command-bot <>
Co-authored-by: Squirrel <gilescope@gmail.com>

* Fixes

* cargo fmt

* Fixes

* Fixes

* Fix CI

* Nfts: Fix Auto-Increment (#12223)

* commit

* passing benchmarks

* clean up

* sync

* runtime implementation

* fix

* fmt

* fix benchmark

* cfg

* remove try-increment-id

* remove unused error

* impl Incrementable for unsigned types

* clean up

* fix in tests

* not needed anymore

* Use OptionQuery

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* Rename Origin to RuntimeOrigin

* [Uniques V2] Tips (#12168)

* Allow to add tips when buying an NFT

* Chore

* Rework tips feature

* Add weights + benchmarks

* Convert tuple to struct

* Fix benchmark

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

* Update frame/nfts/src/benchmarking.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix benchmarks

* Revert the bounded_vec![] approach

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

Co-authored-by: command-bot <>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* [Uniques V2] Atomic NFTs swap (#12285)

* Atomic NFTs swap

* Fmt

* Fix benchmark

* Rename swap -> atomic_swap

* Update target balance

* Rollback

* Fix

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

* Make desired item optional

* Apply suggestions

* Update frame/nfts/src/features/atomic_swap.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Rename fields

* Optimisation

* Add a comment

* deadline -> maybe_deadline

* Add docs

* Change comments

* Add price direction field

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

* Wrap price and direction

* Fix benchmarks

* Use ensure! instead of if {}

* Make duration param mandatory and limit it to MaxDeadlineDuration

* Make the code safer

* Fix clippy

* Chore

* Remove unused vars

* try

* try 2

* try 3

Co-authored-by: command-bot <>
Co-authored-by: Squirrel <gilescope@gmail.com>

* [Uniques V2] Feature flags (#12367)

* Basics

* WIP: change the data format

* Refactor

* Remove redundant new() method

* Rename settings

* Enable tests

* Chore

* Change params order

* Delete the config on collection removal

* Chore

* Remove redundant system features

* Rename force_item_status to force_collection_status

* Update node runtime

* Chore

* Remove thaw_collection

* Chore

* Connect collection.is_frozen to config

* Allow to lock the collection in a new way

* Move free_holding into settings

* Connect collection's metadata locker to feature flags

* DRY

* Chore

* Connect pallet level feature flags

* Prepare tests for the new changes

* Implement Item settings

* Allow to lock the metadata or attributes of an item

* Common -> Settings

* Extract settings related code to a separate file

* Move feature flag checks inside the do_* methods

* Split settings.rs into parts

* Extract repeated code into macro

* Extract macros into their own file

* Chore

* Fix traits

* Fix traits

* Test SystemFeatures

* Fix benchmarks

* Add missing benchmark

* Fix node/runtime/lib.rs

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

* Keep item's config on burn if it's not empty

* Fix the merge artifacts

* Fmt

* Add SystemFeature::NoSwaps check

* Rename SystemFeatures to PalletFeatures

* Rename errors

* Add docs

* Change error message

* Rework pallet features

* Move macros

* Change comments

* Fmt

* Refactor Incrementable

* Use pub(crate) for do_* functions

* Update comments

* Refactor freeze and lock functions

* Rework Collection config and Item confg api

* Chore

* Make clippy happy

* Chore

* Update comment

* RequiredDeposit => DepositRequired

* Address comments

Co-authored-by: command-bot <>

* [Uniques V2] Refactor roles (#12437)

* Basics

* WIP: change the data format

* Refactor

* Remove redundant new() method

* Rename settings

* Enable tests

* Chore

* Change params order

* Delete the config on collection removal

* Chore

* Remove redundant system features

* Rename force_item_status to force_collection_status

* Update node runtime

* Chore

* Remove thaw_collection

* Chore

* Connect collection.is_frozen to config

* Allow to lock the collection in a new way

* Move free_holding into settings

* Connect collection's metadata locker to feature flags

* DRY

* Chore

* Connect pallet level feature flags

* Prepare tests for the new changes

* Implement Item settings

* Allow to lock the metadata or attributes of an item

* Common -> Settings

* Extract settings related code to a separate file

* Move feature flag checks inside the do_* methods

* Split settings.rs into parts

* Extract repeated code into macro

* Extract macros into their own file

* Chore

* Fix traits

* Fix traits

* Test SystemFeatures

* Fix benchmarks

* Add missing benchmark

* Fix node/runtime/lib.rs

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

* Keep item's config on burn if it's not empty

* Fix the merge artifacts

* Fmt

* Add SystemFeature::NoSwaps check

* Refactor roles structure

* Rename SystemFeatures to PalletFeatures

* Rename errors

* Add docs

* Change error message

* Rework pallet features

* Move macros

* Change comments

* Fmt

* Refactor Incrementable

* Use pub(crate) for do_* functions

* Update comments

* Refactor freeze and lock functions

* Rework Collection config and Item confg api

* Chore

* Make clippy happy

* Chore

* Fix artifacts

* Address comments

* Further refactoring

* Add comments

* Add tests for group_roles_by_account()

* Update frame/nfts/src/impl_nonfungibles.rs

* Add test

* Replace Itertools group_by with a custom implementation

* ItemsNotTransferable => ItemsNonTransferable

* Update frame/nfts/src/features/roles.rs

Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>

* Address PR comments

* Add missed comment

Co-authored-by: command-bot <>
Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>

* Fix copy

* Remove storage_prefix

* Remove transactional

* Update comment

* [Uniques V2] Minting options (#12483)

* Basics

* WIP: change the data format

* Refactor

* Remove redundant new() method

* Rename settings

* Enable tests

* Chore

* Change params order

* Delete the config on collection removal

* Chore

* Remove redundant system features

* Rename force_item_status to force_collection_status

* Update node runtime

* Chore

* Remove thaw_collection

* Chore

* Connect collection.is_frozen to config

* Allow to lock the collection in a new way

* Move free_holding into settings

* Connect collection's metadata locker to feature flags

* DRY

* Chore

* Connect pallet level feature flags

* Prepare tests for the new changes

* Implement Item settings

* Allow to lock the metadata or attributes of an item

* Common -> Settings

* Extract settings related code to a separate file

* Move feature flag checks inside the do_* methods

* Split settings.rs into parts

* Extract repeated code into macro

* Extract macros into their own file

* Chore

* Fix traits

* Fix traits

* Test SystemFeatures

* Fix benchmarks

* Add missing benchmark

* Fix node/runtime/lib.rs

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

* Keep item's config on burn if it's not empty

* Fix the merge artifacts

* Fmt

* Add SystemFeature::NoSwaps check

* Rename SystemFeatures to PalletFeatures

* Rename errors

* Add docs

* Change error message

* Change the format of CollectionConfig to store more data

* Move max supply to the CollectionConfig and allow to change it

* Remove ItemConfig from the mint() function and use the one set in mint settings

* Add different mint options

* Allow to change the mint settings

* Add a force_mint() method

* Check mint params

* Some optimisations

* Cover with tests

* Remove merge artifacts

* Chore

* Use the new has_role() method

* Rework item deposits

* More tests

* Refactoring

* Address comments

* Refactor lock_collection()

* Update frame/nfts/src/types.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Update frame/nfts/src/types.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Private => Issuer

* Add more tests

* Fix benchmarks

* Add benchmarks for new methods

* [Uniques v2] Refactoring (#12570)

* Move do_set_price() and do_buy_item() to buy_sell.rs

* Move approvals to feature file

* Move metadata to feature files

* Move the rest of methods to feature files

* Remove artifacts

* Split force_collection_status into 2 methods

* Fix benchmarks

* Fix benchmarks

* Update deps

Co-authored-by: command-bot <>
Co-authored-by: Squirrel <gilescope@gmail.com>

* [Uniques V2] Smart attributes (#12702)

* Basics

* WIP: change the data format

* Refactor

* Remove redundant new() method

* Rename settings

* Enable tests

* Chore

* Change params order

* Delete the config on collection removal

* Chore

* Remove redundant system features

* Rename force_item_status to force_collection_status

* Update node runtime

* Chore

* Remove thaw_collection

* Chore

* Connect collection.is_frozen to config

* Allow to lock the collection in a new way

* Move free_holding into settings

* Connect collection's metadata locker to feature flags

* DRY

* Chore

* Connect pallet level feature flags

* Prepare tests for the new changes

* Implement Item settings

* Allow to lock the metadata or attributes of an item

* Common -> Settings

* Extract settings related code to a separate file

* Move feature flag checks inside the do_* methods

* Split settings.rs into parts

* Extract repeated code into macro

* Extract macros into their own file

* Chore

* Fix traits

* Fix traits

* Test SystemFeatures

* Fix benchmarks

* Add missing benchmark

* Fix node/runtime/lib.rs

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

* Keep item's config on burn if it's not empty

* Fix the merge artifacts

* Fmt

* Add SystemFeature::NoSwaps check

* Rename SystemFeatures to PalletFeatures

* Rename errors

* Add docs

* Change error message

* Change the format of CollectionConfig to store more data

* Move max supply to the CollectionConfig and allow to change it

* Remove ItemConfig from the mint() function and use the one set in mint settings

* Add different mint options

* Allow to change the mint settings

* Add a force_mint() method

* Check mint params

* Some optimisations

* Cover with tests

* Remove merge artifacts

* Chore

* Use the new has_role() method

* Rework item deposits

* More tests

* Refactoring

* Address comments

* Refactor lock_collection()

* Update frame/nfts/src/types.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Update frame/nfts/src/types.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Private => Issuer

* Add more tests

* Fix benchmarks

* Add benchmarks for new methods

* [Uniques v2] Refactoring (#12570)

* Move do_set_price() and do_buy_item() to buy_sell.rs

* Move approvals to feature file

* Move metadata to feature files

* Move the rest of methods to feature files

* Remove artifacts

* Smart attributes

* Split force_collection_status into 2 methods

* Fix benchmarks

* Fix benchmarks

* Update deps

* Fix merge artifact

* Weights + benchmarks + docs

* Change params order

* Chore

* Update frame/nfts/src/lib.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Update docs

* Update frame/nfts/src/lib.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Add PalletId

* Chore

* Add tests

* More tests

* Add doc

* Update errors snapshots

* Ensure we track the owner_deposit field correctly

Co-authored-by: command-bot <>
Co-authored-by: Squirrel <gilescope@gmail.com>

* [Uniques V2] Final improvements (#12736)

* Use KeyPrefixIterator instead of Box

* Change create_collection()

* Restrict from claiming NFTs twice

* Update Readme

* Remove dead code

* Refactoring

* Update readme

* Fix clippy

* Update frame/nfts/src/lib.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

* Update docs

* Typo

* Fix benchmarks

* Add more docs

* DepositRequired setting should affect only the attributes within the CollectionOwner namespace

* [NFTs] Implement missed methods to set the attributes from other pallets (#12919)

* Implement missed methods to set the attributes from other pallets

* Revert snapshots

* Update snapshot

* Update snapshot

* Revert snapshot changes

* Update snapshots

* Yet another snapshot update..

* Update frame/support/src/traits/tokens/nonfungible_v2.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/support/src/traits/tokens/nonfungible_v2.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/support/src/traits/tokens/nonfungible_v2.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/support/src/traits/tokens/nonfungibles_v2.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/support/src/traits/tokens/nonfungible_v2.rs

* Update frame/nfts/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/support/src/traits/tokens/nonfungibles_v2.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Address comments

* [NFTs] Add the new `owner` param to mint() method (#12997)

* Add the new `owner` param to mint() method

* Fmt

* Address comments

* ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts

* Update frame/nfts/src/common_functions.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/types.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/types.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/types.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/types.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/nfts/src/types.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Add call indexes

* Update snapshots

Co-authored-by: Sergej Sakac <73715684+Szegoo@users.noreply.github.com>
Co-authored-by: Squirrel <gilescope@gmail.com>
Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>
Co-authored-by: command-bot <>
Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
This commit is contained in:
Jegor Sidorenko
2022-12-23 18:07:27 +02:00
committed by GitHub
parent 34eb463d99
commit 0edab31776
36 changed files with 9269 additions and 13 deletions
+718
View File
@@ -0,0 +1,718 @@
// This file is part of Substrate.
// Copyright (C) 2020-2022 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.
//! Nfts pallet benchmarking.
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use enumflags2::{BitFlag, BitFlags};
use frame_benchmarking::{
account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller,
};
use frame_support::{
assert_ok,
dispatch::UnfilteredDispatchable,
traits::{EnsureOrigin, Get},
BoundedVec,
};
use frame_system::RawOrigin as SystemOrigin;
use sp_runtime::traits::{Bounded, One};
use sp_std::prelude::*;
use crate::Pallet as Nfts;
const SEED: u32 = 0;
fn create_collection<T: Config<I>, I: 'static>(
) -> (T::CollectionId, T::AccountId, AccountIdLookupOf<T>) {
let caller: T::AccountId = whitelisted_caller();
let caller_lookup = T::Lookup::unlookup(caller.clone());
let collection = T::Helper::collection(0);
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
assert_ok!(Nfts::<T, I>::force_create(
SystemOrigin::Root.into(),
caller_lookup.clone(),
default_collection_config::<T, I>()
));
(collection, caller, caller_lookup)
}
fn add_collection_metadata<T: Config<I>, I: 'static>() -> (T::AccountId, AccountIdLookupOf<T>) {
let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
if caller != whitelisted_caller() {
whitelist_account!(caller);
}
let caller_lookup = T::Lookup::unlookup(caller.clone());
assert_ok!(Nfts::<T, I>::set_collection_metadata(
SystemOrigin::Signed(caller.clone()).into(),
T::Helper::collection(0),
vec![0; T::StringLimit::get() as usize].try_into().unwrap(),
));
(caller, caller_lookup)
}
fn mint_item<T: Config<I>, I: 'static>(
index: u16,
) -> (T::ItemId, T::AccountId, AccountIdLookupOf<T>) {
let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
if caller != whitelisted_caller() {
whitelist_account!(caller);
}
let caller_lookup = T::Lookup::unlookup(caller.clone());
let item = T::Helper::item(index);
assert_ok!(Nfts::<T, I>::mint(
SystemOrigin::Signed(caller.clone()).into(),
T::Helper::collection(0),
item,
caller_lookup.clone(),
None,
));
(item, caller, caller_lookup)
}
fn add_item_metadata<T: Config<I>, I: 'static>(
item: T::ItemId,
) -> (T::AccountId, AccountIdLookupOf<T>) {
let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
if caller != whitelisted_caller() {
whitelist_account!(caller);
}
let caller_lookup = T::Lookup::unlookup(caller.clone());
assert_ok!(Nfts::<T, I>::set_metadata(
SystemOrigin::Signed(caller.clone()).into(),
T::Helper::collection(0),
item,
vec![0; T::StringLimit::get() as usize].try_into().unwrap(),
));
(caller, caller_lookup)
}
fn add_item_attribute<T: Config<I>, I: 'static>(
item: T::ItemId,
) -> (BoundedVec<u8, T::KeyLimit>, T::AccountId, AccountIdLookupOf<T>) {
let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
if caller != whitelisted_caller() {
whitelist_account!(caller);
}
let caller_lookup = T::Lookup::unlookup(caller.clone());
let key: BoundedVec<_, _> = vec![0; T::KeyLimit::get() as usize].try_into().unwrap();
assert_ok!(Nfts::<T, I>::set_attribute(
SystemOrigin::Signed(caller.clone()).into(),
T::Helper::collection(0),
Some(item),
AttributeNamespace::CollectionOwner,
key.clone(),
vec![0; T::ValueLimit::get() as usize].try_into().unwrap(),
));
(key, caller, caller_lookup)
}
fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
let events = frame_system::Pallet::<T>::events();
let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
// compare to the last event record
let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
assert_eq!(event, &system_event);
}
fn make_collection_config<T: Config<I>, I: 'static>(
disable_settings: BitFlags<CollectionSetting>,
) -> CollectionConfigFor<T, I> {
CollectionConfig {
settings: CollectionSettings::from_disabled(disable_settings),
max_supply: None,
mint_settings: MintSettings::default(),
}
}
fn default_collection_config<T: Config<I>, I: 'static>() -> CollectionConfigFor<T, I> {
make_collection_config::<T, I>(CollectionSetting::empty())
}
fn default_item_config() -> ItemConfig {
ItemConfig { settings: ItemSettings::all_enabled() }
}
benchmarks_instance_pallet! {
create {
let collection = T::Helper::collection(0);
let origin = T::CreateOrigin::successful_origin(&collection);
let caller = T::CreateOrigin::ensure_origin(origin.clone(), &collection).unwrap();
whitelist_account!(caller);
let admin = T::Lookup::unlookup(caller.clone());
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
let call = Call::<T, I>::create { admin, config: default_collection_config::<T, I>() };
}: { call.dispatch_bypass_filter(origin)? }
verify {
assert_last_event::<T, I>(Event::Created { collection: T::Helper::collection(0), creator: caller.clone(), owner: caller }.into());
}
force_create {
let caller: T::AccountId = whitelisted_caller();
let caller_lookup = T::Lookup::unlookup(caller.clone());
}: _(SystemOrigin::Root, caller_lookup, default_collection_config::<T, I>())
verify {
assert_last_event::<T, I>(Event::ForceCreated { collection: T::Helper::collection(0), owner: caller }.into());
}
destroy {
let n in 0 .. 1_000;
let m in 0 .. 1_000;
let a in 0 .. 1_000;
let (collection, caller, _) = create_collection::<T, I>();
add_collection_metadata::<T, I>();
for i in 0..n {
mint_item::<T, I>(i as u16);
}
for i in 0..m {
if !Item::<T, I>::contains_key(collection, T::Helper::item(i as u16)) {
mint_item::<T, I>(i as u16);
}
add_item_metadata::<T, I>(T::Helper::item(i as u16));
}
for i in 0..a {
if !Item::<T, I>::contains_key(collection, T::Helper::item(i as u16)) {
mint_item::<T, I>(i as u16);
}
add_item_attribute::<T, I>(T::Helper::item(i as u16));
}
let witness = Collection::<T, I>::get(collection).unwrap().destroy_witness();
}: _(SystemOrigin::Signed(caller), collection, witness)
verify {
assert_last_event::<T, I>(Event::Destroyed { collection }.into());
}
mint {
let (collection, caller, caller_lookup) = create_collection::<T, I>();
let item = T::Helper::item(0);
}: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup, None)
verify {
assert_last_event::<T, I>(Event::Issued { collection, item, owner: caller }.into());
}
force_mint {
let (collection, caller, caller_lookup) = create_collection::<T, I>();
let item = T::Helper::item(0);
}: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup, default_item_config())
verify {
assert_last_event::<T, I>(Event::Issued { collection, item, owner: caller }.into());
}
burn {
let (collection, caller, caller_lookup) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
}: _(SystemOrigin::Signed(caller.clone()), collection, item, Some(caller_lookup))
verify {
assert_last_event::<T, I>(Event::Burned { collection, item, owner: caller }.into());
}
transfer {
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
let target: T::AccountId = account("target", 0, SEED);
let target_lookup = T::Lookup::unlookup(target.clone());
T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance());
}: _(SystemOrigin::Signed(caller.clone()), collection, item, target_lookup)
verify {
assert_last_event::<T, I>(Event::Transferred { collection, item, from: caller, to: target }.into());
}
redeposit {
let i in 0 .. 5_000;
let (collection, caller, _) = create_collection::<T, I>();
let items = (0..i).map(|x| mint_item::<T, I>(x as u16).0).collect::<Vec<_>>();
Nfts::<T, I>::force_collection_config(
SystemOrigin::Root.into(),
collection,
make_collection_config::<T, I>(CollectionSetting::DepositRequired.into()),
)?;
}: _(SystemOrigin::Signed(caller.clone()), collection, items.clone())
verify {
assert_last_event::<T, I>(Event::Redeposited { collection, successful_items: items }.into());
}
lock_item_transfer {
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
}: _(SystemOrigin::Signed(caller.clone()), T::Helper::collection(0), T::Helper::item(0))
verify {
assert_last_event::<T, I>(Event::ItemTransferLocked { collection: T::Helper::collection(0), item: T::Helper::item(0) }.into());
}
unlock_item_transfer {
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
Nfts::<T, I>::lock_item_transfer(
SystemOrigin::Signed(caller.clone()).into(),
collection,
item,
)?;
}: _(SystemOrigin::Signed(caller.clone()), collection, item)
verify {
assert_last_event::<T, I>(Event::ItemTransferUnlocked { collection, item }.into());
}
lock_collection {
let (collection, caller, _) = create_collection::<T, I>();
let lock_settings = CollectionSettings::from_disabled(
CollectionSetting::TransferableItems |
CollectionSetting::UnlockedMetadata |
CollectionSetting::UnlockedAttributes |
CollectionSetting::UnlockedMaxSupply,
);
}: _(SystemOrigin::Signed(caller.clone()), collection, lock_settings)
verify {
assert_last_event::<T, I>(Event::CollectionLocked { collection }.into());
}
transfer_ownership {
let (collection, caller, _) = create_collection::<T, I>();
let target: T::AccountId = account("target", 0, SEED);
let target_lookup = T::Lookup::unlookup(target.clone());
T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance());
let origin = SystemOrigin::Signed(target.clone()).into();
Nfts::<T, I>::set_accept_ownership(origin, Some(collection))?;
}: _(SystemOrigin::Signed(caller), collection, target_lookup)
verify {
assert_last_event::<T, I>(Event::OwnerChanged { collection, new_owner: target }.into());
}
set_team {
let (collection, caller, _) = create_collection::<T, I>();
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), collection, target0, target1, target2)
verify {
assert_last_event::<T, I>(Event::TeamChanged{
collection,
issuer: account("target", 0, SEED),
admin: account("target", 1, SEED),
freezer: account("target", 2, SEED),
}.into());
}
force_collection_owner {
let (collection, _, _) = create_collection::<T, I>();
let origin = T::ForceOrigin::successful_origin();
let target: T::AccountId = account("target", 0, SEED);
let target_lookup = T::Lookup::unlookup(target.clone());
T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance());
let call = Call::<T, I>::force_collection_owner {
collection,
owner: target_lookup,
};
}: { call.dispatch_bypass_filter(origin)? }
verify {
assert_last_event::<T, I>(Event::OwnerChanged { collection, new_owner: target }.into());
}
force_collection_config {
let (collection, caller, _) = create_collection::<T, I>();
let origin = T::ForceOrigin::successful_origin();
let call = Call::<T, I>::force_collection_config {
collection,
config: make_collection_config::<T, I>(CollectionSetting::DepositRequired.into()),
};
}: { call.dispatch_bypass_filter(origin)? }
verify {
assert_last_event::<T, I>(Event::CollectionConfigChanged { collection }.into());
}
lock_item_properties {
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
let lock_metadata = true;
let lock_attributes = true;
}: _(SystemOrigin::Signed(caller), collection, item, lock_metadata, lock_attributes)
verify {
assert_last_event::<T, I>(Event::ItemPropertiesLocked { collection, item, lock_metadata, lock_attributes }.into());
}
set_attribute {
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 (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
}: _(SystemOrigin::Signed(caller), collection, Some(item), AttributeNamespace::CollectionOwner, key.clone(), value.clone())
verify {
assert_last_event::<T, I>(
Event::AttributeSet {
collection,
maybe_item: Some(item),
namespace: AttributeNamespace::CollectionOwner,
key,
value,
}
.into(),
);
}
force_set_attribute {
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 (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
}: _(SystemOrigin::Root, Some(caller), collection, Some(item), AttributeNamespace::CollectionOwner, key.clone(), value.clone())
verify {
assert_last_event::<T, I>(
Event::AttributeSet {
collection,
maybe_item: Some(item),
namespace: AttributeNamespace::CollectionOwner,
key,
value,
}
.into(),
);
}
clear_attribute {
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
add_item_metadata::<T, I>(item);
let (key, ..) = add_item_attribute::<T, I>(item);
}: _(SystemOrigin::Signed(caller), collection, Some(item), AttributeNamespace::CollectionOwner, key.clone())
verify {
assert_last_event::<T, I>(
Event::AttributeCleared {
collection,
maybe_item: Some(item),
namespace: AttributeNamespace::CollectionOwner,
key,
}.into(),
);
}
approve_item_attributes {
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
let target: T::AccountId = account("target", 0, SEED);
let target_lookup = T::Lookup::unlookup(target.clone());
}: _(SystemOrigin::Signed(caller), collection, item, target_lookup)
verify {
assert_last_event::<T, I>(
Event::ItemAttributesApprovalAdded {
collection,
item,
delegate: target,
}
.into(),
);
}
cancel_item_attributes_approval {
let n in 0 .. 1_000;
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
let target: T::AccountId = account("target", 0, SEED);
let target_lookup = T::Lookup::unlookup(target.clone());
Nfts::<T, I>::approve_item_attributes(
SystemOrigin::Signed(caller.clone()).into(),
collection,
item,
target_lookup.clone(),
)?;
T::Currency::make_free_balance_be(&target, DepositBalanceOf::<T, I>::max_value());
let value: BoundedVec<_, _> = vec![0u8; T::ValueLimit::get() as usize].try_into().unwrap();
for i in 0..n {
let mut key = vec![0u8; T::KeyLimit::get() as usize];
let mut s = Vec::from((i as u16).to_be_bytes());
key.truncate(s.len());
key.append(&mut s);
Nfts::<T, I>::set_attribute(
SystemOrigin::Signed(target.clone()).into(),
T::Helper::collection(0),
Some(item),
AttributeNamespace::Account(target.clone()),
key.try_into().unwrap(),
value.clone(),
)?;
}
let witness = CancelAttributesApprovalWitness { account_attributes: n };
}: _(SystemOrigin::Signed(caller), collection, item, target_lookup, witness)
verify {
assert_last_event::<T, I>(
Event::ItemAttributesApprovalRemoved {
collection,
item,
delegate: target,
}
.into(),
);
}
set_metadata {
let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap();
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
}: _(SystemOrigin::Signed(caller), collection, item, data.clone())
verify {
assert_last_event::<T, I>(Event::ItemMetadataSet { collection, item, data }.into());
}
clear_metadata {
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
add_item_metadata::<T, I>(item);
}: _(SystemOrigin::Signed(caller), collection, item)
verify {
assert_last_event::<T, I>(Event::ItemMetadataCleared { collection, item }.into());
}
set_collection_metadata {
let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap();
let (collection, caller, _) = create_collection::<T, I>();
}: _(SystemOrigin::Signed(caller), collection, data.clone())
verify {
assert_last_event::<T, I>(Event::CollectionMetadataSet { collection, data }.into());
}
clear_collection_metadata {
let (collection, caller, _) = create_collection::<T, I>();
add_collection_metadata::<T, I>();
}: _(SystemOrigin::Signed(caller), collection)
verify {
assert_last_event::<T, I>(Event::CollectionMetadataCleared { collection }.into());
}
approve_transfer {
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
let delegate: T::AccountId = account("delegate", 0, SEED);
let delegate_lookup = T::Lookup::unlookup(delegate.clone());
let deadline = T::BlockNumber::max_value();
}: _(SystemOrigin::Signed(caller.clone()), collection, item, delegate_lookup, Some(deadline))
verify {
assert_last_event::<T, I>(Event::TransferApproved { collection, item, owner: caller, delegate, deadline: Some(deadline) }.into());
}
cancel_approval {
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
let delegate: T::AccountId = account("delegate", 0, SEED);
let delegate_lookup = T::Lookup::unlookup(delegate.clone());
let origin = SystemOrigin::Signed(caller.clone()).into();
let deadline = T::BlockNumber::max_value();
Nfts::<T, I>::approve_transfer(origin, collection, item, delegate_lookup.clone(), Some(deadline))?;
}: _(SystemOrigin::Signed(caller.clone()), collection, item, delegate_lookup)
verify {
assert_last_event::<T, I>(Event::ApprovalCancelled { collection, item, owner: caller, delegate }.into());
}
clear_all_transfer_approvals {
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
let delegate: T::AccountId = account("delegate", 0, SEED);
let delegate_lookup = T::Lookup::unlookup(delegate.clone());
let origin = SystemOrigin::Signed(caller.clone()).into();
let deadline = T::BlockNumber::max_value();
Nfts::<T, I>::approve_transfer(origin, collection, item, delegate_lookup.clone(), Some(deadline))?;
}: _(SystemOrigin::Signed(caller.clone()), collection, item)
verify {
assert_last_event::<T, I>(Event::AllApprovalsCancelled {collection, item, owner: caller}.into());
}
set_accept_ownership {
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
let collection = T::Helper::collection(0);
}: _(SystemOrigin::Signed(caller.clone()), Some(collection))
verify {
assert_last_event::<T, I>(Event::OwnershipAcceptanceChanged {
who: caller,
maybe_collection: Some(collection),
}.into());
}
set_collection_max_supply {
let (collection, caller, _) = create_collection::<T, I>();
}: _(SystemOrigin::Signed(caller.clone()), collection, u32::MAX)
verify {
assert_last_event::<T, I>(Event::CollectionMaxSupplySet {
collection,
max_supply: u32::MAX,
}.into());
}
update_mint_settings {
let (collection, caller, _) = create_collection::<T, I>();
let mint_settings = MintSettings {
mint_type: MintType::HolderOf(T::Helper::collection(0)),
start_block: Some(One::one()),
end_block: Some(One::one()),
price: Some(ItemPrice::<T, I>::from(1u32)),
default_item_settings: ItemSettings::all_enabled(),
};
}: _(SystemOrigin::Signed(caller.clone()), collection, mint_settings)
verify {
assert_last_event::<T, I>(Event::CollectionMintSettingsUpdated { collection }.into());
}
set_price {
let (collection, caller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
let delegate: T::AccountId = account("delegate", 0, SEED);
let delegate_lookup = T::Lookup::unlookup(delegate.clone());
let price = ItemPrice::<T, I>::from(100u32);
}: _(SystemOrigin::Signed(caller.clone()), collection, item, Some(price), Some(delegate_lookup))
verify {
assert_last_event::<T, I>(Event::ItemPriceSet {
collection,
item,
price,
whitelisted_buyer: Some(delegate),
}.into());
}
buy_item {
let (collection, seller, _) = create_collection::<T, I>();
let (item, ..) = mint_item::<T, I>(0);
let buyer: T::AccountId = account("buyer", 0, SEED);
let buyer_lookup = T::Lookup::unlookup(buyer.clone());
let price = ItemPrice::<T, I>::from(0u32);
let origin = SystemOrigin::Signed(seller.clone()).into();
Nfts::<T, I>::set_price(origin, collection, item, Some(price.clone()), Some(buyer_lookup))?;
T::Currency::make_free_balance_be(&buyer, DepositBalanceOf::<T, I>::max_value());
}: _(SystemOrigin::Signed(buyer.clone()), collection, item, price.clone())
verify {
assert_last_event::<T, I>(Event::ItemBought {
collection,
item,
price,
seller,
buyer,
}.into());
}
pay_tips {
let n in 0 .. T::MaxTips::get() as u32;
let amount = BalanceOf::<T, I>::from(100u32);
let caller: T::AccountId = whitelisted_caller();
let collection = T::Helper::collection(0);
let item = T::Helper::item(0);
let tips: BoundedVec<_, _> = vec![
ItemTip
{ collection, item, receiver: caller.clone(), amount }; n as usize
].try_into().unwrap();
}: _(SystemOrigin::Signed(caller.clone()), tips)
verify {
if !n.is_zero() {
assert_last_event::<T, I>(Event::TipSent {
collection,
item,
sender: caller.clone(),
receiver: caller.clone(),
amount,
}.into());
}
}
create_swap {
let (collection, caller, _) = create_collection::<T, I>();
let (item1, ..) = mint_item::<T, I>(0);
let (item2, ..) = mint_item::<T, I>(1);
let price = ItemPrice::<T, I>::from(100u32);
let price_direction = PriceDirection::Receive;
let price_with_direction = PriceWithDirection { amount: price, direction: price_direction };
let duration = T::MaxDeadlineDuration::get();
frame_system::Pallet::<T>::set_block_number(One::one());
}: _(SystemOrigin::Signed(caller.clone()), collection, item1, collection, Some(item2), Some(price_with_direction.clone()), duration)
verify {
let current_block = frame_system::Pallet::<T>::block_number();
assert_last_event::<T, I>(Event::SwapCreated {
offered_collection: collection,
offered_item: item1,
desired_collection: collection,
desired_item: Some(item2),
price: Some(price_with_direction),
deadline: current_block.saturating_add(duration),
}.into());
}
cancel_swap {
let (collection, caller, _) = create_collection::<T, I>();
let (item1, ..) = mint_item::<T, I>(0);
let (item2, ..) = mint_item::<T, I>(1);
let price = ItemPrice::<T, I>::from(100u32);
let origin = SystemOrigin::Signed(caller.clone()).into();
let duration = T::MaxDeadlineDuration::get();
let price_direction = PriceDirection::Receive;
let price_with_direction = PriceWithDirection { amount: price, direction: price_direction };
frame_system::Pallet::<T>::set_block_number(One::one());
Nfts::<T, I>::create_swap(origin, collection, item1, collection, Some(item2), Some(price_with_direction.clone()), duration)?;
}: _(SystemOrigin::Signed(caller.clone()), collection, item1)
verify {
assert_last_event::<T, I>(Event::SwapCancelled {
offered_collection: collection,
offered_item: item1,
desired_collection: collection,
desired_item: Some(item2),
price: Some(price_with_direction),
deadline: duration.saturating_add(One::one()),
}.into());
}
claim_swap {
let (collection, caller, _) = create_collection::<T, I>();
let (item1, ..) = mint_item::<T, I>(0);
let (item2, ..) = mint_item::<T, I>(1);
let price = ItemPrice::<T, I>::from(0u32);
let price_direction = PriceDirection::Receive;
let price_with_direction = PriceWithDirection { amount: price, direction: price_direction };
let duration = T::MaxDeadlineDuration::get();
let target: T::AccountId = account("target", 0, SEED);
let target_lookup = T::Lookup::unlookup(target.clone());
T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance());
let origin = SystemOrigin::Signed(caller.clone());
frame_system::Pallet::<T>::set_block_number(One::one());
Nfts::<T, I>::transfer(origin.clone().into(), collection, item2, target_lookup)?;
Nfts::<T, I>::create_swap(
origin.clone().into(),
collection,
item1,
collection,
Some(item2),
Some(price_with_direction.clone()),
duration,
)?;
}: _(SystemOrigin::Signed(target.clone()), collection, item2, collection, item1, Some(price_with_direction.clone()))
verify {
let current_block = frame_system::Pallet::<T>::block_number();
assert_last_event::<T, I>(Event::SwapClaimed {
sent_collection: collection,
sent_item: item2,
sent_item_owner: target,
received_collection: collection,
received_item: item1,
received_item_owner: caller,
price: Some(price_with_direction),
deadline: duration.saturating_add(One::one()),
}.into());
}
impl_benchmark_test_suite!(Nfts, crate::mock::new_test_ext(), crate::mock::Test);
}
@@ -0,0 +1,42 @@
// This file is part of Substrate.
// Copyright (C) 2017-2022 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 pieces of common functionality.
use super::*;
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Get the owner of the item, if the item exists.
pub fn owner(collection: T::CollectionId, item: T::ItemId) -> Option<T::AccountId> {
Item::<T, I>::get(collection, item).map(|i| i.owner)
}
/// Get the owner of the collection, if the collection exists.
pub fn collection_owner(collection: T::CollectionId) -> Option<T::AccountId> {
Collection::<T, I>::get(collection).map(|i| i.owner)
}
#[cfg(any(test, feature = "runtime-benchmarks"))]
pub fn set_next_id(id: T::CollectionId) {
NextCollectionId::<T, I>::set(Some(id));
}
#[cfg(test)]
pub fn get_next_id() -> T::CollectionId {
NextCollectionId::<T, I>::get().unwrap_or(T::CollectionId::initial_value())
}
}
@@ -0,0 +1,132 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use frame_support::pallet_prelude::*;
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub(crate) fn do_approve_transfer(
maybe_check_origin: Option<T::AccountId>,
collection: T::CollectionId,
item: T::ItemId,
delegate: T::AccountId,
maybe_deadline: Option<<T as SystemConfig>::BlockNumber>,
) -> DispatchResult {
ensure!(
Self::is_pallet_feature_enabled(PalletFeature::Approvals),
Error::<T, I>::MethodDisabled
);
let mut details =
Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
let collection_config = Self::get_collection_config(&collection)?;
ensure!(
collection_config.is_setting_enabled(CollectionSetting::TransferableItems),
Error::<T, I>::ItemsNonTransferable
);
if let Some(check_origin) = maybe_check_origin {
let is_admin = Self::has_role(&collection, &check_origin, CollectionRole::Admin);
let permitted = is_admin || check_origin == details.owner;
ensure!(permitted, Error::<T, I>::NoPermission);
}
let now = frame_system::Pallet::<T>::block_number();
let deadline = maybe_deadline.map(|d| d.saturating_add(now));
details
.approvals
.try_insert(delegate.clone(), deadline)
.map_err(|_| Error::<T, I>::ReachedApprovalLimit)?;
Item::<T, I>::insert(&collection, &item, &details);
Self::deposit_event(Event::TransferApproved {
collection,
item,
owner: details.owner,
delegate,
deadline,
});
Ok(())
}
pub(crate) fn do_cancel_approval(
maybe_check_origin: Option<T::AccountId>,
collection: T::CollectionId,
item: T::ItemId,
delegate: T::AccountId,
) -> DispatchResult {
let mut details =
Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
let maybe_deadline = details.approvals.get(&delegate).ok_or(Error::<T, I>::NotDelegate)?;
let is_past_deadline = if let Some(deadline) = maybe_deadline {
let now = frame_system::Pallet::<T>::block_number();
now > *deadline
} else {
false
};
if !is_past_deadline {
if let Some(check_origin) = maybe_check_origin {
let is_admin = Self::has_role(&collection, &check_origin, CollectionRole::Admin);
let permitted = is_admin || check_origin == details.owner;
ensure!(permitted, Error::<T, I>::NoPermission);
}
}
details.approvals.remove(&delegate);
Item::<T, I>::insert(&collection, &item, &details);
Self::deposit_event(Event::ApprovalCancelled {
collection,
item,
owner: details.owner,
delegate,
});
Ok(())
}
pub(crate) fn do_clear_all_transfer_approvals(
maybe_check_origin: Option<T::AccountId>,
collection: T::CollectionId,
item: T::ItemId,
) -> DispatchResult {
let mut details =
Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownCollection)?;
if let Some(check_origin) = maybe_check_origin {
let is_admin = Self::has_role(&collection, &check_origin, CollectionRole::Admin);
let permitted = is_admin || check_origin == details.owner;
ensure!(permitted, Error::<T, I>::NoPermission);
}
details.approvals.clear();
Item::<T, I>::insert(&collection, &item, &details);
Self::deposit_event(Event::AllApprovalsCancelled {
collection,
item,
owner: details.owner,
});
Ok(())
}
}
@@ -0,0 +1,184 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use frame_support::{
pallet_prelude::*,
traits::{Currency, ExistenceRequirement::KeepAlive},
};
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub(crate) fn do_create_swap(
caller: T::AccountId,
offered_collection_id: T::CollectionId,
offered_item_id: T::ItemId,
desired_collection_id: T::CollectionId,
maybe_desired_item_id: Option<T::ItemId>,
maybe_price: Option<PriceWithDirection<ItemPrice<T, I>>>,
duration: <T as SystemConfig>::BlockNumber,
) -> DispatchResult {
ensure!(
Self::is_pallet_feature_enabled(PalletFeature::Swaps),
Error::<T, I>::MethodDisabled
);
ensure!(duration <= T::MaxDeadlineDuration::get(), Error::<T, I>::WrongDuration);
let item = Item::<T, I>::get(&offered_collection_id, &offered_item_id)
.ok_or(Error::<T, I>::UnknownItem)?;
ensure!(item.owner == caller, Error::<T, I>::NoPermission);
match maybe_desired_item_id {
Some(desired_item_id) => ensure!(
Item::<T, I>::contains_key(&desired_collection_id, &desired_item_id),
Error::<T, I>::UnknownItem
),
None => ensure!(
Collection::<T, I>::contains_key(&desired_collection_id),
Error::<T, I>::UnknownCollection
),
};
let now = frame_system::Pallet::<T>::block_number();
let deadline = duration.saturating_add(now);
PendingSwapOf::<T, I>::insert(
&offered_collection_id,
&offered_item_id,
PendingSwap {
desired_collection: desired_collection_id,
desired_item: maybe_desired_item_id,
price: maybe_price.clone(),
deadline,
},
);
Self::deposit_event(Event::SwapCreated {
offered_collection: offered_collection_id,
offered_item: offered_item_id,
desired_collection: desired_collection_id,
desired_item: maybe_desired_item_id,
price: maybe_price,
deadline,
});
Ok(())
}
pub(crate) fn do_cancel_swap(
caller: T::AccountId,
offered_collection_id: T::CollectionId,
offered_item_id: T::ItemId,
) -> DispatchResult {
let swap = PendingSwapOf::<T, I>::get(&offered_collection_id, &offered_item_id)
.ok_or(Error::<T, I>::UnknownSwap)?;
let now = frame_system::Pallet::<T>::block_number();
if swap.deadline > now {
let item = Item::<T, I>::get(&offered_collection_id, &offered_item_id)
.ok_or(Error::<T, I>::UnknownItem)?;
ensure!(item.owner == caller, Error::<T, I>::NoPermission);
}
PendingSwapOf::<T, I>::remove(&offered_collection_id, &offered_item_id);
Self::deposit_event(Event::SwapCancelled {
offered_collection: offered_collection_id,
offered_item: offered_item_id,
desired_collection: swap.desired_collection,
desired_item: swap.desired_item,
price: swap.price,
deadline: swap.deadline,
});
Ok(())
}
pub(crate) fn do_claim_swap(
caller: T::AccountId,
send_collection_id: T::CollectionId,
send_item_id: T::ItemId,
receive_collection_id: T::CollectionId,
receive_item_id: T::ItemId,
witness_price: Option<PriceWithDirection<ItemPrice<T, I>>>,
) -> DispatchResult {
ensure!(
Self::is_pallet_feature_enabled(PalletFeature::Swaps),
Error::<T, I>::MethodDisabled
);
let send_item = Item::<T, I>::get(&send_collection_id, &send_item_id)
.ok_or(Error::<T, I>::UnknownItem)?;
let receive_item = Item::<T, I>::get(&receive_collection_id, &receive_item_id)
.ok_or(Error::<T, I>::UnknownItem)?;
let swap = PendingSwapOf::<T, I>::get(&receive_collection_id, &receive_item_id)
.ok_or(Error::<T, I>::UnknownSwap)?;
ensure!(send_item.owner == caller, Error::<T, I>::NoPermission);
ensure!(
swap.desired_collection == send_collection_id && swap.price == witness_price,
Error::<T, I>::UnknownSwap
);
if let Some(desired_item) = swap.desired_item {
ensure!(desired_item == send_item_id, Error::<T, I>::UnknownSwap);
}
let now = frame_system::Pallet::<T>::block_number();
ensure!(now <= swap.deadline, Error::<T, I>::DeadlineExpired);
if let Some(ref price) = swap.price {
match price.direction {
PriceDirection::Send => T::Currency::transfer(
&receive_item.owner,
&send_item.owner,
price.amount,
KeepAlive,
)?,
PriceDirection::Receive => T::Currency::transfer(
&send_item.owner,
&receive_item.owner,
price.amount,
KeepAlive,
)?,
};
}
// This also removes the swap.
Self::do_transfer(send_collection_id, send_item_id, receive_item.owner.clone(), |_, _| {
Ok(())
})?;
Self::do_transfer(
receive_collection_id,
receive_item_id,
send_item.owner.clone(),
|_, _| Ok(()),
)?;
Self::deposit_event(Event::SwapClaimed {
sent_collection: send_collection_id,
sent_item: send_item_id,
sent_item_owner: send_item.owner,
received_collection: receive_collection_id,
received_item: receive_item_id,
received_item_owner: receive_item.owner,
price: swap.price,
deadline: swap.deadline,
});
Ok(())
}
}
@@ -0,0 +1,323 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use frame_support::pallet_prelude::*;
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub(crate) fn do_set_attribute(
origin: T::AccountId,
collection: T::CollectionId,
maybe_item: Option<T::ItemId>,
namespace: AttributeNamespace<T::AccountId>,
key: BoundedVec<u8, T::KeyLimit>,
value: BoundedVec<u8, T::ValueLimit>,
) -> DispatchResult {
ensure!(
Self::is_pallet_feature_enabled(PalletFeature::Attributes),
Error::<T, I>::MethodDisabled
);
let mut collection_details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
ensure!(
Self::is_valid_namespace(
&origin,
&namespace,
&collection,
&collection_details.owner,
&maybe_item,
)?,
Error::<T, I>::NoPermission
);
let collection_config = Self::get_collection_config(&collection)?;
// for the `CollectionOwner` namespace we need to check if the collection/item is not locked
match namespace {
AttributeNamespace::CollectionOwner => match maybe_item {
None => {
ensure!(
collection_config.is_setting_enabled(CollectionSetting::UnlockedAttributes),
Error::<T, I>::LockedCollectionAttributes
)
},
Some(item) => {
let maybe_is_locked = Self::get_item_config(&collection, &item)
.map(|c| c.has_disabled_setting(ItemSetting::UnlockedAttributes))?;
ensure!(!maybe_is_locked, Error::<T, I>::LockedItemAttributes);
},
},
_ => (),
}
let attribute = Attribute::<T, I>::get((collection, maybe_item, &namespace, &key));
if attribute.is_none() {
collection_details.attributes.saturating_inc();
}
let old_deposit =
attribute.map_or(AttributeDeposit { account: None, amount: Zero::zero() }, |m| m.1);
let mut deposit = Zero::zero();
if collection_config.is_setting_enabled(CollectionSetting::DepositRequired) ||
namespace != AttributeNamespace::CollectionOwner
{
deposit = T::DepositPerByte::get()
.saturating_mul(((key.len() + value.len()) as u32).into())
.saturating_add(T::AttributeDepositBase::get());
}
// NOTE: when we transfer an item, we don't move attributes in the ItemOwner namespace.
// When the new owner updates the same attribute, we will update the depositor record
// and return the deposit to the previous owner.
if old_deposit.account.is_some() && old_deposit.account != Some(origin.clone()) {
T::Currency::unreserve(&old_deposit.account.unwrap(), old_deposit.amount);
T::Currency::reserve(&origin, deposit)?;
} else if deposit > old_deposit.amount {
T::Currency::reserve(&origin, deposit - old_deposit.amount)?;
} else if deposit < old_deposit.amount {
T::Currency::unreserve(&origin, old_deposit.amount - deposit);
}
// NOTE: we don't track the depositor in the CollectionOwner namespace as it's always a
// collection's owner. This simplifies the collection's transfer to another owner.
let deposit_owner = match namespace {
AttributeNamespace::CollectionOwner => {
collection_details.owner_deposit.saturating_accrue(deposit);
collection_details.owner_deposit.saturating_reduce(old_deposit.amount);
None
},
_ => Some(origin),
};
Attribute::<T, I>::insert(
(&collection, maybe_item, &namespace, &key),
(&value, AttributeDeposit { account: deposit_owner, amount: deposit }),
);
Collection::<T, I>::insert(collection, &collection_details);
Self::deposit_event(Event::AttributeSet { collection, maybe_item, key, value, namespace });
Ok(())
}
pub(crate) fn do_force_set_attribute(
set_as: Option<T::AccountId>,
collection: T::CollectionId,
maybe_item: Option<T::ItemId>,
namespace: AttributeNamespace<T::AccountId>,
key: BoundedVec<u8, T::KeyLimit>,
value: BoundedVec<u8, T::ValueLimit>,
) -> DispatchResult {
let mut collection_details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
let attribute = Attribute::<T, I>::get((collection, maybe_item, &namespace, &key));
if let Some((_, deposit)) = attribute {
if deposit.account != set_as && deposit.amount != Zero::zero() {
if let Some(deposit_account) = deposit.account {
T::Currency::unreserve(&deposit_account, deposit.amount);
}
}
} else {
collection_details.attributes.saturating_inc();
}
Attribute::<T, I>::insert(
(&collection, maybe_item, &namespace, &key),
(&value, AttributeDeposit { account: set_as, amount: Zero::zero() }),
);
Collection::<T, I>::insert(collection, &collection_details);
Self::deposit_event(Event::AttributeSet { collection, maybe_item, key, value, namespace });
Ok(())
}
pub(crate) fn do_clear_attribute(
maybe_check_owner: Option<T::AccountId>,
collection: T::CollectionId,
maybe_item: Option<T::ItemId>,
namespace: AttributeNamespace<T::AccountId>,
key: BoundedVec<u8, T::KeyLimit>,
) -> DispatchResult {
if let Some((_, deposit)) =
Attribute::<T, I>::take((collection, maybe_item, &namespace, &key))
{
let mut collection_details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
if let Some(check_owner) = &maybe_check_owner {
if deposit.account != maybe_check_owner {
ensure!(
Self::is_valid_namespace(
&check_owner,
&namespace,
&collection,
&collection_details.owner,
&maybe_item,
)?,
Error::<T, I>::NoPermission
);
}
// can't clear `CollectionOwner` type attributes if the collection/item is locked
match namespace {
AttributeNamespace::CollectionOwner => match maybe_item {
None => {
let collection_config = Self::get_collection_config(&collection)?;
ensure!(
collection_config
.is_setting_enabled(CollectionSetting::UnlockedAttributes),
Error::<T, I>::LockedCollectionAttributes
)
},
Some(item) => {
// NOTE: if the item was previously burned, the ItemConfigOf record
// might not exist. In that case, we allow to clear the attribute.
let maybe_is_locked = Self::get_item_config(&collection, &item)
.map_or(false, |c| {
c.has_disabled_setting(ItemSetting::UnlockedAttributes)
});
ensure!(!maybe_is_locked, Error::<T, I>::LockedItemAttributes);
},
},
_ => (),
};
}
collection_details.attributes.saturating_dec();
match namespace {
AttributeNamespace::CollectionOwner => {
collection_details.owner_deposit.saturating_reduce(deposit.amount);
T::Currency::unreserve(&collection_details.owner, deposit.amount);
},
_ => (),
};
if let Some(deposit_account) = deposit.account {
T::Currency::unreserve(&deposit_account, deposit.amount);
}
Collection::<T, I>::insert(collection, &collection_details);
Self::deposit_event(Event::AttributeCleared { collection, maybe_item, key, namespace });
}
Ok(())
}
pub(crate) fn do_approve_item_attributes(
check_origin: T::AccountId,
collection: T::CollectionId,
item: T::ItemId,
delegate: T::AccountId,
) -> DispatchResult {
ensure!(
Self::is_pallet_feature_enabled(PalletFeature::Attributes),
Error::<T, I>::MethodDisabled
);
let details = Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
ensure!(check_origin == details.owner, Error::<T, I>::NoPermission);
ItemAttributesApprovalsOf::<T, I>::try_mutate(collection, item, |approvals| {
approvals
.try_insert(delegate.clone())
.map_err(|_| Error::<T, I>::ReachedApprovalLimit)?;
Self::deposit_event(Event::ItemAttributesApprovalAdded { collection, item, delegate });
Ok(())
})
}
pub(crate) fn do_cancel_item_attributes_approval(
check_origin: T::AccountId,
collection: T::CollectionId,
item: T::ItemId,
delegate: T::AccountId,
witness: CancelAttributesApprovalWitness,
) -> DispatchResult {
ensure!(
Self::is_pallet_feature_enabled(PalletFeature::Attributes),
Error::<T, I>::MethodDisabled
);
let details = Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
ensure!(check_origin == details.owner, Error::<T, I>::NoPermission);
ItemAttributesApprovalsOf::<T, I>::try_mutate(collection, item, |approvals| {
approvals.remove(&delegate);
let mut attributes: u32 = 0;
let mut deposited: DepositBalanceOf<T, I> = Zero::zero();
for (_, (_, deposit)) in Attribute::<T, I>::drain_prefix((
&collection,
Some(item),
AttributeNamespace::Account(delegate.clone()),
)) {
attributes.saturating_inc();
deposited = deposited.saturating_add(deposit.amount);
}
ensure!(attributes <= witness.account_attributes, Error::<T, I>::BadWitness);
if !deposited.is_zero() {
T::Currency::unreserve(&delegate, deposited);
}
Self::deposit_event(Event::ItemAttributesApprovalRemoved {
collection,
item,
delegate,
});
Ok(())
})
}
fn is_valid_namespace(
origin: &T::AccountId,
namespace: &AttributeNamespace<T::AccountId>,
collection: &T::CollectionId,
collection_owner: &T::AccountId,
maybe_item: &Option<T::ItemId>,
) -> Result<bool, DispatchError> {
let mut result = false;
match namespace {
AttributeNamespace::CollectionOwner => result = origin == collection_owner,
AttributeNamespace::ItemOwner =>
if let Some(item) = maybe_item {
let item_details =
Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
result = origin == &item_details.owner
},
AttributeNamespace::Account(account_id) =>
if let Some(item) = maybe_item {
let approvals = ItemAttributesApprovalsOf::<T, I>::get(&collection, &item);
result = account_id == origin && approvals.contains(&origin)
},
_ => (),
};
Ok(result)
}
/// A helper method to construct attribute's key.
pub fn construct_attribute_key(
key: Vec<u8>,
) -> Result<BoundedVec<u8, T::KeyLimit>, DispatchError> {
Ok(BoundedVec::try_from(key).map_err(|_| Error::<T, I>::IncorrectData)?)
}
/// A helper method to construct attribute's value.
pub fn construct_attribute_value(
value: Vec<u8>,
) -> Result<BoundedVec<u8, T::ValueLimit>, DispatchError> {
Ok(BoundedVec::try_from(value).map_err(|_| Error::<T, I>::IncorrectData)?)
}
}
@@ -0,0 +1,130 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use frame_support::{
pallet_prelude::*,
traits::{Currency, ExistenceRequirement, ExistenceRequirement::KeepAlive},
};
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub(crate) fn do_pay_tips(
sender: T::AccountId,
tips: BoundedVec<ItemTipOf<T, I>, T::MaxTips>,
) -> DispatchResult {
for tip in tips {
let ItemTip { collection, item, receiver, amount } = tip;
T::Currency::transfer(&sender, &receiver, amount, KeepAlive)?;
Self::deposit_event(Event::TipSent {
collection,
item,
sender: sender.clone(),
receiver,
amount,
});
}
Ok(())
}
pub(crate) fn do_set_price(
collection: T::CollectionId,
item: T::ItemId,
sender: T::AccountId,
price: Option<ItemPrice<T, I>>,
whitelisted_buyer: Option<T::AccountId>,
) -> DispatchResult {
ensure!(
Self::is_pallet_feature_enabled(PalletFeature::Trading),
Error::<T, I>::MethodDisabled
);
let details = Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
ensure!(details.owner == sender, Error::<T, I>::NoPermission);
let collection_config = Self::get_collection_config(&collection)?;
ensure!(
collection_config.is_setting_enabled(CollectionSetting::TransferableItems),
Error::<T, I>::ItemsNonTransferable
);
let item_config = Self::get_item_config(&collection, &item)?;
ensure!(
item_config.is_setting_enabled(ItemSetting::Transferable),
Error::<T, I>::ItemLocked
);
if let Some(ref price) = price {
ItemPriceOf::<T, I>::insert(&collection, &item, (price, whitelisted_buyer.clone()));
Self::deposit_event(Event::ItemPriceSet {
collection,
item,
price: *price,
whitelisted_buyer,
});
} else {
ItemPriceOf::<T, I>::remove(&collection, &item);
Self::deposit_event(Event::ItemPriceRemoved { collection, item });
}
Ok(())
}
pub(crate) fn do_buy_item(
collection: T::CollectionId,
item: T::ItemId,
buyer: T::AccountId,
bid_price: ItemPrice<T, I>,
) -> DispatchResult {
ensure!(
Self::is_pallet_feature_enabled(PalletFeature::Trading),
Error::<T, I>::MethodDisabled
);
let details = Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
ensure!(details.owner != buyer, Error::<T, I>::NoPermission);
let price_info =
ItemPriceOf::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::NotForSale)?;
ensure!(bid_price >= price_info.0, Error::<T, I>::BidTooLow);
if let Some(only_buyer) = price_info.1 {
ensure!(only_buyer == buyer, Error::<T, I>::NoPermission);
}
T::Currency::transfer(
&buyer,
&details.owner,
price_info.0,
ExistenceRequirement::KeepAlive,
)?;
let old_owner = details.owner.clone();
Self::do_transfer(collection, item, buyer.clone(), |_, _| Ok(()))?;
Self::deposit_event(Event::ItemBought {
collection,
item,
price: price_info.0,
seller: old_owner,
buyer,
});
Ok(())
}
}
@@ -0,0 +1,118 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use frame_support::pallet_prelude::*;
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub fn do_create_collection(
collection: T::CollectionId,
owner: T::AccountId,
admin: T::AccountId,
config: CollectionConfigFor<T, I>,
deposit: DepositBalanceOf<T, I>,
event: Event<T, I>,
) -> DispatchResult {
ensure!(!Collection::<T, I>::contains_key(collection), Error::<T, I>::CollectionIdInUse);
T::Currency::reserve(&owner, deposit)?;
Collection::<T, I>::insert(
collection,
CollectionDetails {
owner: owner.clone(),
owner_deposit: deposit,
items: 0,
item_metadatas: 0,
attributes: 0,
},
);
CollectionRoleOf::<T, I>::insert(
collection,
admin,
CollectionRoles(
CollectionRole::Admin | CollectionRole::Freezer | CollectionRole::Issuer,
),
);
let next_id = collection.increment();
CollectionConfigOf::<T, I>::insert(&collection, config);
CollectionAccount::<T, I>::insert(&owner, &collection, ());
NextCollectionId::<T, I>::set(Some(next_id));
Self::deposit_event(Event::NextCollectionIdIncremented { next_id });
Self::deposit_event(event);
Ok(())
}
pub fn do_destroy_collection(
collection: T::CollectionId,
witness: DestroyWitness,
maybe_check_owner: Option<T::AccountId>,
) -> Result<DestroyWitness, DispatchError> {
Collection::<T, I>::try_mutate_exists(collection, |maybe_details| {
let collection_details =
maybe_details.take().ok_or(Error::<T, I>::UnknownCollection)?;
if let Some(check_owner) = maybe_check_owner {
ensure!(collection_details.owner == check_owner, Error::<T, I>::NoPermission);
}
ensure!(collection_details.items == witness.items, Error::<T, I>::BadWitness);
ensure!(
collection_details.item_metadatas == witness.item_metadatas,
Error::<T, I>::BadWitness
);
ensure!(collection_details.attributes == witness.attributes, Error::<T, I>::BadWitness);
for (item, details) in Item::<T, I>::drain_prefix(&collection) {
Account::<T, I>::remove((&details.owner, &collection, &item));
T::Currency::unreserve(&details.deposit.account, details.deposit.amount);
}
#[allow(deprecated)]
ItemMetadataOf::<T, I>::remove_prefix(&collection, None);
#[allow(deprecated)]
ItemPriceOf::<T, I>::remove_prefix(&collection, None);
#[allow(deprecated)]
PendingSwapOf::<T, I>::remove_prefix(&collection, None);
CollectionMetadataOf::<T, I>::remove(&collection);
Self::clear_roles(&collection)?;
for (_, (_, deposit)) in Attribute::<T, I>::drain_prefix((&collection,)) {
if !deposit.amount.is_zero() {
if let Some(account) = deposit.account {
T::Currency::unreserve(&account, deposit.amount);
}
}
}
CollectionAccount::<T, I>::remove(&collection_details.owner, &collection);
T::Currency::unreserve(&collection_details.owner, collection_details.owner_deposit);
CollectionConfigOf::<T, I>::remove(&collection);
let _ = ItemConfigOf::<T, I>::clear_prefix(&collection, witness.items, None);
let _ =
ItemAttributesApprovalsOf::<T, I>::clear_prefix(&collection, witness.items, None);
Self::deposit_event(Event::Destroyed { collection });
Ok(DestroyWitness {
items: collection_details.items,
item_metadatas: collection_details.item_metadatas,
attributes: collection_details.attributes,
})
})
}
}
@@ -0,0 +1,126 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use frame_support::pallet_prelude::*;
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub fn do_mint(
collection: T::CollectionId,
item: T::ItemId,
depositor: T::AccountId,
mint_to: T::AccountId,
item_config: ItemConfig,
deposit_collection_owner: bool,
with_details_and_config: impl FnOnce(
&CollectionDetailsFor<T, I>,
&CollectionConfigFor<T, I>,
) -> DispatchResult,
) -> DispatchResult {
ensure!(!Item::<T, I>::contains_key(collection, item), Error::<T, I>::AlreadyExists);
Collection::<T, I>::try_mutate(
&collection,
|maybe_collection_details| -> DispatchResult {
let collection_details =
maybe_collection_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
let collection_config = Self::get_collection_config(&collection)?;
with_details_and_config(collection_details, &collection_config)?;
if let Some(max_supply) = collection_config.max_supply {
ensure!(collection_details.items < max_supply, Error::<T, I>::MaxSupplyReached);
}
collection_details.items.saturating_inc();
let collection_config = Self::get_collection_config(&collection)?;
let deposit_amount = match collection_config
.is_setting_enabled(CollectionSetting::DepositRequired)
{
true => T::ItemDeposit::get(),
false => Zero::zero(),
};
let deposit_account = match deposit_collection_owner {
true => collection_details.owner.clone(),
false => depositor,
};
let item_owner = mint_to.clone();
Account::<T, I>::insert((&item_owner, &collection, &item), ());
if let Ok(existing_config) = ItemConfigOf::<T, I>::try_get(&collection, &item) {
ensure!(existing_config == item_config, Error::<T, I>::InconsistentItemConfig);
} else {
ItemConfigOf::<T, I>::insert(&collection, &item, item_config);
}
T::Currency::reserve(&deposit_account, deposit_amount)?;
let deposit = ItemDeposit { account: deposit_account, amount: deposit_amount };
let details = ItemDetails {
owner: item_owner,
approvals: ApprovalsOf::<T, I>::default(),
deposit,
};
Item::<T, I>::insert(&collection, &item, details);
Ok(())
},
)?;
Self::deposit_event(Event::Issued { collection, item, owner: mint_to });
Ok(())
}
pub fn do_burn(
collection: T::CollectionId,
item: T::ItemId,
with_details: impl FnOnce(&ItemDetailsFor<T, I>) -> DispatchResult,
) -> DispatchResult {
let owner = Collection::<T, I>::try_mutate(
&collection,
|maybe_collection_details| -> Result<T::AccountId, DispatchError> {
let collection_details =
maybe_collection_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
let details = Item::<T, I>::get(&collection, &item)
.ok_or(Error::<T, I>::UnknownCollection)?;
with_details(&details)?;
// Return the deposit.
T::Currency::unreserve(&details.deposit.account, details.deposit.amount);
collection_details.items.saturating_dec();
Ok(details.owner)
},
)?;
Item::<T, I>::remove(&collection, &item);
Account::<T, I>::remove((&owner, &collection, &item));
ItemPriceOf::<T, I>::remove(&collection, &item);
PendingSwapOf::<T, I>::remove(&collection, &item);
ItemAttributesApprovalsOf::<T, I>::remove(&collection, &item);
// NOTE: if item's settings are not empty (e.g. item's metadata is locked)
// then we keep the record and don't remove it
let config = Self::get_item_config(&collection, &item)?;
if !config.has_disabled_settings() {
ItemConfigOf::<T, I>::remove(&collection, &item);
}
Self::deposit_event(Event::Burned { collection, item, owner });
Ok(())
}
}
+120
View File
@@ -0,0 +1,120 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use frame_support::pallet_prelude::*;
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub(crate) fn do_lock_collection(
origin: T::AccountId,
collection: T::CollectionId,
lock_settings: CollectionSettings,
) -> DispatchResult {
ensure!(
Self::has_role(&collection, &origin, CollectionRole::Freezer),
Error::<T, I>::NoPermission
);
ensure!(
!lock_settings.is_disabled(CollectionSetting::DepositRequired),
Error::<T, I>::WrongSetting
);
CollectionConfigOf::<T, I>::try_mutate(collection, |maybe_config| {
let config = maybe_config.as_mut().ok_or(Error::<T, I>::NoConfig)?;
for setting in lock_settings.get_disabled() {
config.disable_setting(setting);
}
Self::deposit_event(Event::<T, I>::CollectionLocked { collection });
Ok(())
})
}
pub(crate) fn do_lock_item_transfer(
origin: T::AccountId,
collection: T::CollectionId,
item: T::ItemId,
) -> DispatchResult {
ensure!(
Self::has_role(&collection, &origin, CollectionRole::Freezer),
Error::<T, I>::NoPermission
);
let mut config = Self::get_item_config(&collection, &item)?;
if !config.has_disabled_setting(ItemSetting::Transferable) {
config.disable_setting(ItemSetting::Transferable);
}
ItemConfigOf::<T, I>::insert(&collection, &item, config);
Self::deposit_event(Event::<T, I>::ItemTransferLocked { collection, item });
Ok(())
}
pub(crate) fn do_unlock_item_transfer(
origin: T::AccountId,
collection: T::CollectionId,
item: T::ItemId,
) -> DispatchResult {
ensure!(
Self::has_role(&collection, &origin, CollectionRole::Freezer),
Error::<T, I>::NoPermission
);
let mut config = Self::get_item_config(&collection, &item)?;
if config.has_disabled_setting(ItemSetting::Transferable) {
config.enable_setting(ItemSetting::Transferable);
}
ItemConfigOf::<T, I>::insert(&collection, &item, config);
Self::deposit_event(Event::<T, I>::ItemTransferUnlocked { collection, item });
Ok(())
}
pub(crate) fn do_lock_item_properties(
maybe_check_owner: Option<T::AccountId>,
collection: T::CollectionId,
item: T::ItemId,
lock_metadata: bool,
lock_attributes: bool,
) -> DispatchResult {
let collection_details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
if let Some(check_owner) = &maybe_check_owner {
ensure!(check_owner == &collection_details.owner, Error::<T, I>::NoPermission);
}
ItemConfigOf::<T, I>::try_mutate(collection, item, |maybe_config| {
let config = maybe_config.as_mut().ok_or(Error::<T, I>::UnknownItem)?;
if lock_metadata {
config.disable_setting(ItemSetting::UnlockedMetadata);
}
if lock_attributes {
config.disable_setting(ItemSetting::UnlockedAttributes);
}
Self::deposit_event(Event::<T, I>::ItemPropertiesLocked {
collection,
item,
lock_metadata,
lock_attributes,
});
Ok(())
})
}
}
@@ -0,0 +1,173 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use frame_support::pallet_prelude::*;
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub(crate) fn do_set_item_metadata(
maybe_check_owner: Option<T::AccountId>,
collection: T::CollectionId,
item: T::ItemId,
data: BoundedVec<u8, T::StringLimit>,
) -> DispatchResult {
let mut collection_details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
let item_config = Self::get_item_config(&collection, &item)?;
ensure!(
maybe_check_owner.is_none() ||
item_config.is_setting_enabled(ItemSetting::UnlockedMetadata),
Error::<T, I>::LockedItemMetadata
);
if let Some(check_owner) = &maybe_check_owner {
ensure!(check_owner == &collection_details.owner, Error::<T, I>::NoPermission);
}
let collection_config = Self::get_collection_config(&collection)?;
ItemMetadataOf::<T, I>::try_mutate_exists(collection, item, |metadata| {
if metadata.is_none() {
collection_details.item_metadatas.saturating_inc();
}
let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
collection_details.owner_deposit.saturating_reduce(old_deposit);
let mut deposit = Zero::zero();
if collection_config.is_setting_enabled(CollectionSetting::DepositRequired) &&
maybe_check_owner.is_some()
{
deposit = T::DepositPerByte::get()
.saturating_mul(((data.len()) as u32).into())
.saturating_add(T::MetadataDepositBase::get());
}
if deposit > old_deposit {
T::Currency::reserve(&collection_details.owner, deposit - old_deposit)?;
} else if deposit < old_deposit {
T::Currency::unreserve(&collection_details.owner, old_deposit - deposit);
}
collection_details.owner_deposit.saturating_accrue(deposit);
*metadata = Some(ItemMetadata { deposit, data: data.clone() });
Collection::<T, I>::insert(&collection, &collection_details);
Self::deposit_event(Event::ItemMetadataSet { collection, item, data });
Ok(())
})
}
pub(crate) fn do_clear_item_metadata(
maybe_check_owner: Option<T::AccountId>,
collection: T::CollectionId,
item: T::ItemId,
) -> DispatchResult {
let mut collection_details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
if let Some(check_owner) = &maybe_check_owner {
ensure!(check_owner == &collection_details.owner, Error::<T, I>::NoPermission);
}
// NOTE: if the item was previously burned, the ItemConfigOf record might not exist
let is_locked = Self::get_item_config(&collection, &item)
.map_or(false, |c| c.has_disabled_setting(ItemSetting::UnlockedMetadata));
ensure!(maybe_check_owner.is_none() || !is_locked, Error::<T, I>::LockedItemMetadata);
ItemMetadataOf::<T, I>::try_mutate_exists(collection, item, |metadata| {
if metadata.is_some() {
collection_details.item_metadatas.saturating_dec();
}
let deposit = metadata.take().ok_or(Error::<T, I>::UnknownItem)?.deposit;
T::Currency::unreserve(&collection_details.owner, deposit);
collection_details.owner_deposit.saturating_reduce(deposit);
Collection::<T, I>::insert(&collection, &collection_details);
Self::deposit_event(Event::ItemMetadataCleared { collection, item });
Ok(())
})
}
pub(crate) fn do_set_collection_metadata(
maybe_check_owner: Option<T::AccountId>,
collection: T::CollectionId,
data: BoundedVec<u8, T::StringLimit>,
) -> DispatchResult {
let collection_config = Self::get_collection_config(&collection)?;
ensure!(
maybe_check_owner.is_none() ||
collection_config.is_setting_enabled(CollectionSetting::UnlockedMetadata),
Error::<T, I>::LockedCollectionMetadata
);
let mut details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
if let Some(check_owner) = &maybe_check_owner {
ensure!(check_owner == &details.owner, Error::<T, I>::NoPermission);
}
CollectionMetadataOf::<T, I>::try_mutate_exists(collection, |metadata| {
let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
details.owner_deposit.saturating_reduce(old_deposit);
let mut deposit = Zero::zero();
if maybe_check_owner.is_some() &&
collection_config.is_setting_enabled(CollectionSetting::DepositRequired)
{
deposit = T::DepositPerByte::get()
.saturating_mul(((data.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.owner_deposit.saturating_accrue(deposit);
Collection::<T, I>::insert(&collection, details);
*metadata = Some(CollectionMetadata { deposit, data: data.clone() });
Self::deposit_event(Event::CollectionMetadataSet { collection, data });
Ok(())
})
}
pub(crate) fn do_clear_collection_metadata(
maybe_check_owner: Option<T::AccountId>,
collection: T::CollectionId,
) -> DispatchResult {
let details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
if let Some(check_owner) = &maybe_check_owner {
ensure!(check_owner == &details.owner, Error::<T, I>::NoPermission);
}
let collection_config = Self::get_collection_config(&collection)?;
ensure!(
maybe_check_owner.is_none() ||
collection_config.is_setting_enabled(CollectionSetting::UnlockedMetadata),
Error::<T, I>::LockedCollectionMetadata
);
CollectionMetadataOf::<T, I>::try_mutate_exists(collection, |metadata| {
let deposit = metadata.take().ok_or(Error::<T, I>::UnknownCollection)?.deposit;
T::Currency::unreserve(&details.owner, deposit);
Self::deposit_event(Event::CollectionMetadataCleared { collection });
Ok(())
})
}
}
+28
View File
@@ -0,0 +1,28 @@
// This file is part of Substrate.
// Copyright (C) 2022 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.
pub mod approvals;
pub mod atomic_swap;
pub mod attributes;
pub mod buy_sell;
pub mod create_delete_collection;
pub mod create_delete_item;
pub mod lock;
pub mod metadata;
pub mod roles;
pub mod settings;
pub mod transfer;
@@ -0,0 +1,99 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use frame_support::pallet_prelude::*;
use sp_std::collections::btree_map::BTreeMap;
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub(crate) fn do_set_team(
maybe_check_owner: Option<T::AccountId>,
collection: T::CollectionId,
issuer: T::AccountId,
admin: T::AccountId,
freezer: T::AccountId,
) -> DispatchResult {
Collection::<T, I>::try_mutate(collection, |maybe_details| {
let details = maybe_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
if let Some(check_origin) = maybe_check_owner {
ensure!(check_origin == details.owner, Error::<T, I>::NoPermission);
}
// delete previous values
Self::clear_roles(&collection)?;
let account_to_role = Self::group_roles_by_account(vec![
(issuer.clone(), CollectionRole::Issuer),
(admin.clone(), CollectionRole::Admin),
(freezer.clone(), CollectionRole::Freezer),
]);
for (account, roles) in account_to_role {
CollectionRoleOf::<T, I>::insert(&collection, &account, roles);
}
Self::deposit_event(Event::TeamChanged { collection, issuer, admin, freezer });
Ok(())
})
}
/// Clears all the roles in a specified collection.
///
/// - `collection_id`: A collection to clear the roles in.
///
/// Throws an error if some of the roles were left in storage.
/// This means the `CollectionRoles::max_roles()` needs to be adjusted.
pub(crate) fn clear_roles(collection_id: &T::CollectionId) -> Result<(), DispatchError> {
let res = CollectionRoleOf::<T, I>::clear_prefix(
&collection_id,
CollectionRoles::max_roles() as u32,
None,
);
ensure!(res.maybe_cursor.is_none(), Error::<T, I>::RolesNotCleared);
Ok(())
}
/// Returns true if a specified account has a provided role within that collection.
///
/// - `collection_id`: A collection to check the role in.
/// - `account_id`: An account to check the role for.
/// - `role`: A role to validate.
///
/// Returns boolean.
pub(crate) fn has_role(
collection_id: &T::CollectionId,
account_id: &T::AccountId,
role: CollectionRole,
) -> bool {
CollectionRoleOf::<T, I>::get(&collection_id, &account_id)
.map_or(false, |roles| roles.has_role(role))
}
/// Groups provided roles by account, given one account could have multiple roles.
///
/// - `input`: A vector of (Account, Role) tuples.
///
/// Returns a grouped vector.
pub fn group_roles_by_account(
input: Vec<(T::AccountId, CollectionRole)>,
) -> Vec<(T::AccountId, CollectionRoles)> {
let mut result = BTreeMap::new();
for (account, role) in input.into_iter() {
result.entry(account).or_insert(CollectionRoles::none()).add_role(role);
}
result.into_iter().collect()
}
}
@@ -0,0 +1,103 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use frame_support::pallet_prelude::*;
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub(crate) fn do_force_collection_config(
collection: T::CollectionId,
config: CollectionConfigFor<T, I>,
) -> DispatchResult {
ensure!(Collection::<T, I>::contains_key(&collection), Error::<T, I>::UnknownCollection);
CollectionConfigOf::<T, I>::insert(&collection, config);
Self::deposit_event(Event::CollectionConfigChanged { collection });
Ok(())
}
pub(crate) fn do_set_collection_max_supply(
maybe_check_owner: Option<T::AccountId>,
collection: T::CollectionId,
max_supply: u32,
) -> DispatchResult {
let collection_config = Self::get_collection_config(&collection)?;
ensure!(
collection_config.is_setting_enabled(CollectionSetting::UnlockedMaxSupply),
Error::<T, I>::MaxSupplyLocked
);
let details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
if let Some(check_owner) = &maybe_check_owner {
ensure!(check_owner == &details.owner, Error::<T, I>::NoPermission);
}
ensure!(details.items <= max_supply, Error::<T, I>::MaxSupplyTooSmall);
CollectionConfigOf::<T, I>::try_mutate(collection, |maybe_config| {
let config = maybe_config.as_mut().ok_or(Error::<T, I>::NoConfig)?;
config.max_supply = Some(max_supply);
Self::deposit_event(Event::CollectionMaxSupplySet { collection, max_supply });
Ok(())
})
}
pub(crate) fn do_update_mint_settings(
maybe_check_owner: Option<T::AccountId>,
collection: T::CollectionId,
mint_settings: MintSettings<
BalanceOf<T, I>,
<T as SystemConfig>::BlockNumber,
T::CollectionId,
>,
) -> DispatchResult {
let details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
if let Some(check_owner) = &maybe_check_owner {
ensure!(check_owner == &details.owner, Error::<T, I>::NoPermission);
}
CollectionConfigOf::<T, I>::try_mutate(collection, |maybe_config| {
let config = maybe_config.as_mut().ok_or(Error::<T, I>::NoConfig)?;
config.mint_settings = mint_settings;
Self::deposit_event(Event::CollectionMintSettingsUpdated { collection });
Ok(())
})
}
pub(crate) fn get_collection_config(
collection_id: &T::CollectionId,
) -> Result<CollectionConfigFor<T, I>, DispatchError> {
let config =
CollectionConfigOf::<T, I>::get(&collection_id).ok_or(Error::<T, I>::NoConfig)?;
Ok(config)
}
pub(crate) fn get_item_config(
collection_id: &T::CollectionId,
item_id: &T::ItemId,
) -> Result<ItemConfig, DispatchError> {
let config = ItemConfigOf::<T, I>::get(&collection_id, &item_id)
.ok_or(Error::<T, I>::UnknownItem)?;
Ok(config)
}
pub(crate) fn is_pallet_feature_enabled(feature: PalletFeature) -> bool {
let features = T::Features::get();
return features.is_enabled(feature)
}
}
@@ -0,0 +1,166 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use frame_support::pallet_prelude::*;
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub fn do_transfer(
collection: T::CollectionId,
item: T::ItemId,
dest: T::AccountId,
with_details: impl FnOnce(
&CollectionDetailsFor<T, I>,
&mut ItemDetailsFor<T, I>,
) -> DispatchResult,
) -> DispatchResult {
let collection_details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
ensure!(!T::Locker::is_locked(collection, item), Error::<T, I>::ItemLocked);
let collection_config = Self::get_collection_config(&collection)?;
ensure!(
collection_config.is_setting_enabled(CollectionSetting::TransferableItems),
Error::<T, I>::ItemsNonTransferable
);
let item_config = Self::get_item_config(&collection, &item)?;
ensure!(
item_config.is_setting_enabled(ItemSetting::Transferable),
Error::<T, I>::ItemLocked
);
let mut details =
Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
with_details(&collection_details, &mut details)?;
if details.deposit.account == details.owner {
// Move the deposit to the new owner.
T::Currency::repatriate_reserved(
&details.owner,
&dest,
details.deposit.amount,
Reserved,
)?;
}
Account::<T, I>::remove((&details.owner, &collection, &item));
Account::<T, I>::insert((&dest, &collection, &item), ());
let origin = details.owner;
details.owner = dest;
// The approved accounts have to be reset to None, because otherwise pre-approve attack
// would be possible, where the owner can approve his second account before making the
// transaction and then claiming the item back.
details.approvals.clear();
Item::<T, I>::insert(&collection, &item, &details);
ItemPriceOf::<T, I>::remove(&collection, &item);
PendingSwapOf::<T, I>::remove(&collection, &item);
Self::deposit_event(Event::Transferred {
collection,
item,
from: origin,
to: details.owner,
});
Ok(())
}
pub(crate) fn do_transfer_ownership(
origin: T::AccountId,
collection: T::CollectionId,
owner: T::AccountId,
) -> DispatchResult {
let acceptable_collection = OwnershipAcceptance::<T, I>::get(&owner);
ensure!(acceptable_collection.as_ref() == Some(&collection), Error::<T, I>::Unaccepted);
Collection::<T, I>::try_mutate(collection, |maybe_details| {
let details = maybe_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
ensure!(origin == details.owner, Error::<T, I>::NoPermission);
if details.owner == owner {
return Ok(())
}
// Move the deposit to the new owner.
T::Currency::repatriate_reserved(
&details.owner,
&owner,
details.owner_deposit,
Reserved,
)?;
CollectionAccount::<T, I>::remove(&details.owner, &collection);
CollectionAccount::<T, I>::insert(&owner, &collection, ());
details.owner = owner.clone();
OwnershipAcceptance::<T, I>::remove(&owner);
Self::deposit_event(Event::OwnerChanged { collection, new_owner: owner });
Ok(())
})
}
pub(crate) fn do_set_accept_ownership(
who: T::AccountId,
maybe_collection: Option<T::CollectionId>,
) -> DispatchResult {
let old = OwnershipAcceptance::<T, I>::get(&who);
match (old.is_some(), maybe_collection.is_some()) {
(false, true) => {
frame_system::Pallet::<T>::inc_consumers(&who)?;
},
(true, false) => {
frame_system::Pallet::<T>::dec_consumers(&who);
},
_ => {},
}
if let Some(collection) = maybe_collection.as_ref() {
OwnershipAcceptance::<T, I>::insert(&who, collection);
} else {
OwnershipAcceptance::<T, I>::remove(&who);
}
Self::deposit_event(Event::OwnershipAcceptanceChanged { who, maybe_collection });
Ok(())
}
pub(crate) fn do_force_collection_owner(
collection: T::CollectionId,
owner: T::AccountId,
) -> DispatchResult {
Collection::<T, I>::try_mutate(collection, |maybe_details| {
let details = maybe_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
if details.owner == owner {
return Ok(())
}
// Move the deposit to the new owner.
T::Currency::repatriate_reserved(
&details.owner,
&owner,
details.owner_deposit,
Reserved,
)?;
CollectionAccount::<T, I>::remove(&details.owner, &collection);
CollectionAccount::<T, I>::insert(&owner, &collection, ());
details.owner = owner.clone();
Self::deposit_event(Event::OwnerChanged { collection, new_owner: owner });
Ok(())
})
}
}
@@ -0,0 +1,289 @@
// This file is part of Substrate.
// Copyright (C) 2017-2022 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 `nonfungibles` traits.
use super::*;
use frame_support::{
ensure,
storage::KeyPrefixIterator,
traits::{tokens::nonfungibles_v2::*, Get},
BoundedSlice,
};
use sp_runtime::{DispatchError, DispatchResult};
use sp_std::prelude::*;
impl<T: Config<I>, I: 'static> Inspect<<T as SystemConfig>::AccountId> for Pallet<T, I> {
type ItemId = T::ItemId;
type CollectionId = T::CollectionId;
fn owner(
collection: &Self::CollectionId,
item: &Self::ItemId,
) -> Option<<T as SystemConfig>::AccountId> {
Item::<T, I>::get(collection, item).map(|a| a.owner)
}
fn collection_owner(collection: &Self::CollectionId) -> Option<<T as SystemConfig>::AccountId> {
Collection::<T, I>::get(collection).map(|a| a.owner)
}
/// Returns the attribute value of `item` of `collection` corresponding to `key`.
///
/// When `key` is empty, we return the item metadata value.
///
/// By default this is `None`; no attributes are defined.
fn attribute(
collection: &Self::CollectionId,
item: &Self::ItemId,
namespace: &AttributeNamespace<<T as SystemConfig>::AccountId>,
key: &[u8],
) -> Option<Vec<u8>> {
if key.is_empty() {
// We make the empty key map to the item metadata value.
ItemMetadataOf::<T, I>::get(collection, item).map(|m| m.data.into())
} else {
let key = BoundedSlice::<_, _>::try_from(key).ok()?;
Attribute::<T, I>::get((collection, Some(item), namespace, key)).map(|a| a.0.into())
}
}
/// Returns the attribute value of `item` of `collection` corresponding to `key`.
///
/// When `key` is empty, we return the item metadata value.
///
/// By default this is `None`; no attributes are defined.
fn collection_attribute(collection: &Self::CollectionId, key: &[u8]) -> Option<Vec<u8>> {
if key.is_empty() {
// We make the empty key map to the item metadata value.
CollectionMetadataOf::<T, I>::get(collection).map(|m| m.data.into())
} else {
let key = BoundedSlice::<_, _>::try_from(key).ok()?;
Attribute::<T, I>::get((
collection,
Option::<T::ItemId>::None,
AttributeNamespace::CollectionOwner,
key,
))
.map(|a| a.0.into())
}
}
/// Returns `true` if the `item` of `collection` may be transferred.
///
/// Default implementation is that all items are transferable.
fn can_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> bool {
match (
CollectionConfigOf::<T, I>::get(collection),
ItemConfigOf::<T, I>::get(collection, item),
) {
(Some(cc), Some(ic))
if cc.is_setting_enabled(CollectionSetting::TransferableItems) &&
ic.is_setting_enabled(ItemSetting::Transferable) =>
true,
_ => false,
}
}
}
impl<T: Config<I>, I: 'static> Create<<T as SystemConfig>::AccountId, CollectionConfigFor<T, I>>
for Pallet<T, I>
{
/// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`.
fn create_collection(
who: &T::AccountId,
admin: &T::AccountId,
config: &CollectionConfigFor<T, I>,
) -> Result<T::CollectionId, DispatchError> {
// DepositRequired can be disabled by calling the force_create() only
ensure!(
!config.has_disabled_setting(CollectionSetting::DepositRequired),
Error::<T, I>::WrongSetting
);
let collection =
NextCollectionId::<T, I>::get().unwrap_or(T::CollectionId::initial_value());
Self::do_create_collection(
collection,
who.clone(),
admin.clone(),
*config,
T::CollectionDeposit::get(),
Event::Created { collection, creator: who.clone(), owner: admin.clone() },
)?;
Ok(collection)
}
}
impl<T: Config<I>, I: 'static> Destroy<<T as SystemConfig>::AccountId> for Pallet<T, I> {
type DestroyWitness = DestroyWitness;
fn get_destroy_witness(collection: &Self::CollectionId) -> Option<DestroyWitness> {
Collection::<T, I>::get(collection).map(|a| a.destroy_witness())
}
fn destroy(
collection: Self::CollectionId,
witness: Self::DestroyWitness,
maybe_check_owner: Option<T::AccountId>,
) -> Result<Self::DestroyWitness, DispatchError> {
Self::do_destroy_collection(collection, witness, maybe_check_owner)
}
}
impl<T: Config<I>, I: 'static> Mutate<<T as SystemConfig>::AccountId, ItemConfig> for Pallet<T, I> {
fn mint_into(
collection: &Self::CollectionId,
item: &Self::ItemId,
who: &T::AccountId,
item_config: &ItemConfig,
deposit_collection_owner: bool,
) -> DispatchResult {
Self::do_mint(
*collection,
*item,
who.clone(),
who.clone(),
*item_config,
deposit_collection_owner,
|_, _| Ok(()),
)
}
fn burn(
collection: &Self::CollectionId,
item: &Self::ItemId,
maybe_check_owner: Option<&T::AccountId>,
) -> DispatchResult {
Self::do_burn(*collection, *item, |d| {
if let Some(check_owner) = maybe_check_owner {
if &d.owner != check_owner {
return Err(Error::<T, I>::NoPermission.into())
}
}
Ok(())
})
}
fn set_attribute(
collection: &Self::CollectionId,
item: &Self::ItemId,
key: &[u8],
value: &[u8],
) -> DispatchResult {
Self::do_force_set_attribute(
None,
*collection,
Some(*item),
AttributeNamespace::Pallet,
Self::construct_attribute_key(key.to_vec())?,
Self::construct_attribute_value(value.to_vec())?,
)
}
fn set_typed_attribute<K: Encode, V: Encode>(
collection: &Self::CollectionId,
item: &Self::ItemId,
key: &K,
value: &V,
) -> DispatchResult {
key.using_encoded(|k| {
value.using_encoded(|v| {
<Self as Mutate<T::AccountId, ItemConfig>>::set_attribute(collection, item, k, v)
})
})
}
fn set_collection_attribute(
collection: &Self::CollectionId,
key: &[u8],
value: &[u8],
) -> DispatchResult {
Self::do_force_set_attribute(
None,
*collection,
None,
AttributeNamespace::Pallet,
Self::construct_attribute_key(key.to_vec())?,
Self::construct_attribute_value(value.to_vec())?,
)
}
fn set_typed_collection_attribute<K: Encode, V: Encode>(
collection: &Self::CollectionId,
key: &K,
value: &V,
) -> DispatchResult {
key.using_encoded(|k| {
value.using_encoded(|v| {
<Self as Mutate<T::AccountId, ItemConfig>>::set_collection_attribute(
collection, k, v,
)
})
})
}
}
impl<T: Config<I>, I: 'static> Transfer<T::AccountId> for Pallet<T, I> {
fn transfer(
collection: &Self::CollectionId,
item: &Self::ItemId,
destination: &T::AccountId,
) -> DispatchResult {
Self::do_transfer(*collection, *item, destination.clone(), |_, _| Ok(()))
}
}
impl<T: Config<I>, I: 'static> InspectEnumerable<T::AccountId> for Pallet<T, I> {
type CollectionsIterator = KeyPrefixIterator<<T as Config<I>>::CollectionId>;
type ItemsIterator = KeyPrefixIterator<<T as Config<I>>::ItemId>;
type OwnedIterator =
KeyPrefixIterator<(<T as Config<I>>::CollectionId, <T as Config<I>>::ItemId)>;
type OwnedInCollectionIterator = KeyPrefixIterator<<T as Config<I>>::ItemId>;
/// Returns an iterator of the collections in existence.
///
/// NOTE: iterating this list invokes a storage read per item.
fn collections() -> Self::CollectionsIterator {
Collection::<T, I>::iter_keys()
}
/// Returns an iterator of the items of a `collection` in existence.
///
/// NOTE: iterating this list invokes a storage read per item.
fn items(collection: &Self::CollectionId) -> Self::ItemsIterator {
Item::<T, I>::iter_key_prefix(collection)
}
/// Returns an iterator of the items of all collections owned by `who`.
///
/// NOTE: iterating this list invokes a storage read per item.
fn owned(who: &T::AccountId) -> Self::OwnedIterator {
Account::<T, I>::iter_key_prefix((who,))
}
/// Returns an iterator of the items of `collection` owned by `who`.
///
/// NOTE: iterating this list invokes a storage read per item.
fn owned_in_collection(
collection: &Self::CollectionId,
who: &T::AccountId,
) -> Self::OwnedInCollectionIterator {
Account::<T, I>::iter_key_prefix((who, collection))
}
}
File diff suppressed because it is too large Load Diff
+74
View File
@@ -0,0 +1,74 @@
// This file is part of Substrate.
// Copyright (C) 2022 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.
macro_rules! impl_incrementable {
($($type:ty),+) => {
$(
impl Incrementable for $type {
fn increment(&self) -> Self {
let mut val = self.clone();
val.saturating_inc();
val
}
fn initial_value() -> Self {
0
}
}
)+
};
}
pub(crate) use impl_incrementable;
macro_rules! impl_codec_bitflags {
($wrapper:ty, $size:ty, $bitflag_enum:ty) => {
impl MaxEncodedLen for $wrapper {
fn max_encoded_len() -> usize {
<$size>::max_encoded_len()
}
}
impl Encode for $wrapper {
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
self.0.bits().using_encoded(f)
}
}
impl EncodeLike for $wrapper {}
impl Decode for $wrapper {
fn decode<I: codec::Input>(
input: &mut I,
) -> sp_std::result::Result<Self, codec::Error> {
let field = <$size>::decode(input)?;
Ok(Self(BitFlags::from_bits(field as $size).map_err(|_| "invalid value")?))
}
}
impl TypeInfo for $wrapper {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("BitFlags", module_path!()))
.type_params(vec![TypeParameter::new("T", Some(meta_type::<$bitflag_enum>()))])
.composite(
Fields::unnamed()
.field(|f| f.ty::<$size>().type_name(stringify!($bitflag_enum))),
)
}
}
};
}
pub(crate) use impl_codec_bitflags;
+123
View File
@@ -0,0 +1,123 @@
// This file is part of Substrate.
// Copyright (C) 2019-2022 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 Nfts pallet.
use super::*;
use crate as pallet_nfts;
use frame_support::{
construct_runtime, parameter_types,
traits::{AsEnsureOriginWithArg, ConstU32, ConstU64},
};
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
};
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
construct_runtime!(
pub enum Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
Nfts: pallet_nfts::{Pallet, Call, Storage, Event<T>},
}
);
impl frame_system::Config for Test {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ConstU64<250>;
type DbWeight = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<u64>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}
impl pallet_balances::Config for Test {
type Balance = u64;
type DustRemoval = ();
type RuntimeEvent = RuntimeEvent;
type ExistentialDeposit = ConstU64<1>;
type AccountStore = System;
type WeightInfo = ();
type MaxLocks = ();
type MaxReserves = ConstU32<50>;
type ReserveIdentifier = [u8; 8];
}
parameter_types! {
pub storage Features: PalletFeatures = PalletFeatures::all_enabled();
}
impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type CollectionId = u32;
type ItemId = u32;
type Currency = Balances;
type CreateOrigin = AsEnsureOriginWithArg<frame_system::EnsureSigned<u64>>;
type ForceOrigin = frame_system::EnsureRoot<u64>;
type Locker = ();
type CollectionDeposit = ConstU64<2>;
type ItemDeposit = ConstU64<1>;
type MetadataDepositBase = ConstU64<1>;
type AttributeDepositBase = ConstU64<1>;
type DepositPerByte = ConstU64<1>;
type StringLimit = ConstU32<50>;
type KeyLimit = ConstU32<50>;
type ValueLimit = ConstU32<50>;
type ApprovalsLimit = ConstU32<10>;
type ItemAttributesApprovalsLimit = ConstU32<2>;
type MaxTips = ConstU32<10>;
type MaxDeadlineDuration = ConstU64<10000>;
type Features = Features;
type WeightInfo = ();
#[cfg(feature = "runtime-benchmarks")]
type Helper = ();
}
pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
let t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}
File diff suppressed because it is too large Load Diff
+465
View File
@@ -0,0 +1,465 @@
// This file is part of Substrate.
// Copyright (C) 2017-2022 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 Nfts pallet.
use super::*;
use crate::macros::*;
use codec::EncodeLike;
use enumflags2::{bitflags, BitFlags};
use frame_support::{
pallet_prelude::{BoundedVec, MaxEncodedLen},
traits::Get,
BoundedBTreeMap, BoundedBTreeSet,
};
use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter};
pub(super) type DepositBalanceOf<T, I = ()> =
<<T as Config<I>>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;
pub(super) type CollectionDetailsFor<T, I> =
CollectionDetails<<T as SystemConfig>::AccountId, DepositBalanceOf<T, I>>;
pub(super) type ApprovalsOf<T, I = ()> = BoundedBTreeMap<
<T as SystemConfig>::AccountId,
Option<<T as SystemConfig>::BlockNumber>,
<T as Config<I>>::ApprovalsLimit,
>;
pub(super) type ItemAttributesApprovals<T, I = ()> =
BoundedBTreeSet<<T as SystemConfig>::AccountId, <T as Config<I>>::ItemAttributesApprovalsLimit>;
pub(super) type ItemDepositOf<T, I> =
ItemDeposit<DepositBalanceOf<T, I>, <T as SystemConfig>::AccountId>;
pub(super) type AttributeDepositOf<T, I> =
AttributeDeposit<DepositBalanceOf<T, I>, <T as SystemConfig>::AccountId>;
pub(super) type ItemDetailsFor<T, I> =
ItemDetails<<T as SystemConfig>::AccountId, ItemDepositOf<T, I>, ApprovalsOf<T, I>>;
pub(super) type BalanceOf<T, I = ()> =
<<T as Config<I>>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;
pub(super) type ItemPrice<T, I = ()> = BalanceOf<T, I>;
pub(super) type ItemTipOf<T, I = ()> = ItemTip<
<T as Config<I>>::CollectionId,
<T as Config<I>>::ItemId,
<T as SystemConfig>::AccountId,
BalanceOf<T, I>,
>;
pub(super) type CollectionConfigFor<T, I = ()> = CollectionConfig<
BalanceOf<T, I>,
<T as SystemConfig>::BlockNumber,
<T as Config<I>>::CollectionId,
>;
pub trait Incrementable {
fn increment(&self) -> Self;
fn initial_value() -> Self;
}
impl_incrementable!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
/// Information about a collection.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct CollectionDetails<AccountId, DepositBalance> {
/// Collection's owner.
pub(super) owner: AccountId,
/// The total balance deposited by the owner for all the storage data associated with this
/// collection. Used by `destroy`.
pub(super) owner_deposit: DepositBalance,
/// The total number of outstanding items of this collection.
pub(super) items: u32,
/// The total number of outstanding item metadata of this collection.
pub(super) item_metadatas: u32,
/// The total number of attributes for this collection.
pub(super) attributes: u32,
}
/// Witness data for the destroy transactions.
#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct DestroyWitness {
/// The total number of outstanding items of this collection.
#[codec(compact)]
pub items: u32,
/// The total number of items in this collection that have outstanding item metadata.
#[codec(compact)]
pub item_metadatas: u32,
/// The total number of attributes for this collection.
#[codec(compact)]
pub attributes: u32,
}
impl<AccountId, DepositBalance> CollectionDetails<AccountId, DepositBalance> {
pub fn destroy_witness(&self) -> DestroyWitness {
DestroyWitness {
items: self.items,
item_metadatas: self.item_metadatas,
attributes: self.attributes,
}
}
}
/// Witness data for items mint transactions.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
pub struct MintWitness<ItemId> {
/// Provide the id of the item in a required collection.
pub owner_of_item: ItemId,
}
/// Information concerning the ownership of a single unique item.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)]
pub struct ItemDetails<AccountId, Deposit, Approvals> {
/// The owner of this item.
pub(super) owner: AccountId,
/// The approved transferrer of this item, if one is set.
pub(super) approvals: Approvals,
/// The amount held in the pallet's default account for this item. Free-hold items will have
/// this as zero.
pub(super) deposit: Deposit,
}
/// Information about the reserved item deposit.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct ItemDeposit<DepositBalance, AccountId> {
/// A depositor account.
pub(super) account: AccountId,
/// An amount that gets reserved.
pub(super) amount: DepositBalance,
}
/// Information about the collection's metadata.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(StringLimit))]
#[codec(mel_bound(DepositBalance: MaxEncodedLen))]
pub struct CollectionMetadata<DepositBalance, StringLimit: Get<u32>> {
/// The balance deposited for this metadata.
///
/// This pays for the data stored in this struct.
pub(super) deposit: DepositBalance,
/// General information concerning this collection. 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: BoundedVec<u8, StringLimit>,
}
/// Information about the item's metadata.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(StringLimit))]
#[codec(mel_bound(DepositBalance: MaxEncodedLen))]
pub struct ItemMetadata<DepositBalance, StringLimit: Get<u32>> {
/// The balance deposited for this metadata.
///
/// This pays for the data stored in this struct.
pub(super) deposit: DepositBalance,
/// General information concerning this item. 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: BoundedVec<u8, StringLimit>,
}
/// Information about the tip.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct ItemTip<CollectionId, ItemId, AccountId, Amount> {
/// The collection of the item.
pub(super) collection: CollectionId,
/// An item of which the tip is sent for.
pub(super) item: ItemId,
/// A sender of the tip.
pub(super) receiver: AccountId,
/// An amount the sender is willing to tip.
pub(super) amount: Amount,
}
/// Information about the pending swap.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)]
pub struct PendingSwap<CollectionId, ItemId, ItemPriceWithDirection, Deadline> {
/// The collection that contains the item that the user wants to receive.
pub(super) desired_collection: CollectionId,
/// The item the user wants to receive.
pub(super) desired_item: Option<ItemId>,
/// A price for the desired `item` with the direction.
pub(super) price: Option<ItemPriceWithDirection>,
/// An optional deadline for the swap.
pub(super) deadline: Deadline,
}
/// Information about the reserved attribute deposit.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct AttributeDeposit<DepositBalance, AccountId> {
/// A depositor account.
pub(super) account: Option<AccountId>,
/// An amount that gets reserved.
pub(super) amount: DepositBalance,
}
/// Specifies whether the tokens will be sent or received.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub enum PriceDirection {
/// Tokens will be sent.
Send,
/// Tokens will be received.
Receive,
}
/// Holds the details about the price.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct PriceWithDirection<Amount> {
/// An amount.
pub(super) amount: Amount,
/// A direction (send or receive).
pub(super) direction: PriceDirection,
}
/// Support for up to 64 user-enabled features on a collection.
#[bitflags]
#[repr(u64)]
#[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub enum CollectionSetting {
/// Items in this collection are transferable.
TransferableItems,
/// The metadata of this collection can be modified.
UnlockedMetadata,
/// Attributes of this collection can be modified.
UnlockedAttributes,
/// The supply of this collection can be modified.
UnlockedMaxSupply,
/// When this isn't set then the deposit is required to hold the items of this collection.
DepositRequired,
}
/// Wrapper type for `BitFlags<CollectionSetting>` that implements `Codec`.
#[derive(Clone, Copy, PartialEq, Eq, Default, RuntimeDebug)]
pub struct CollectionSettings(pub BitFlags<CollectionSetting>);
impl CollectionSettings {
pub fn all_enabled() -> Self {
Self(BitFlags::EMPTY)
}
pub fn get_disabled(&self) -> BitFlags<CollectionSetting> {
self.0
}
pub fn is_disabled(&self, setting: CollectionSetting) -> bool {
self.0.contains(setting)
}
pub fn from_disabled(settings: BitFlags<CollectionSetting>) -> Self {
Self(settings)
}
}
impl_codec_bitflags!(CollectionSettings, u64, CollectionSetting);
/// Mint type. Can the NFT be create by anyone, or only the creator of the collection,
/// or only by wallets that already hold an NFT from a certain collection?
/// The ownership of a privately minted NFT is still publicly visible.
#[derive(Clone, Copy, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub enum MintType<CollectionId> {
/// Only an `Issuer` could mint items.
Issuer,
/// Anyone could mint items.
Public,
/// Only holders of items in specified collection could mint new items.
HolderOf(CollectionId),
}
/// Holds the information about minting.
#[derive(Clone, Copy, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct MintSettings<Price, BlockNumber, CollectionId> {
/// Whether anyone can mint or if minters are restricted to some subset.
pub(super) mint_type: MintType<CollectionId>,
/// An optional price per mint.
pub(super) price: Option<Price>,
/// When the mint starts.
pub(super) start_block: Option<BlockNumber>,
/// When the mint ends.
pub(super) end_block: Option<BlockNumber>,
/// Default settings each item will get during the mint.
pub(super) default_item_settings: ItemSettings,
}
impl<Price, BlockNumber, CollectionId> Default for MintSettings<Price, BlockNumber, CollectionId> {
fn default() -> Self {
Self {
mint_type: MintType::Issuer,
price: None,
start_block: None,
end_block: None,
default_item_settings: ItemSettings::all_enabled(),
}
}
}
/// A witness data to cancel attributes approval operation.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
pub struct CancelAttributesApprovalWitness {
/// An amount of attributes previously created by account.
pub account_attributes: u32,
}
/// A list of possible pallet-level attributes.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub enum PalletAttributes<CollectionId> {
/// Marks an item as being used in order to claim another item.
UsedToClaim(CollectionId),
}
/// Collection's configuration.
#[derive(
Clone, Copy, Decode, Default, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo,
)]
pub struct CollectionConfig<Price, BlockNumber, CollectionId> {
/// Collection's settings.
pub(super) settings: CollectionSettings,
/// Collection's max supply.
pub(super) max_supply: Option<u32>,
/// Default settings each item will get during the mint.
pub(super) mint_settings: MintSettings<Price, BlockNumber, CollectionId>,
}
impl<Price, BlockNumber, CollectionId> CollectionConfig<Price, BlockNumber, CollectionId> {
pub fn is_setting_enabled(&self, setting: CollectionSetting) -> bool {
!self.settings.is_disabled(setting)
}
pub fn has_disabled_setting(&self, setting: CollectionSetting) -> bool {
self.settings.is_disabled(setting)
}
pub fn enable_setting(&mut self, setting: CollectionSetting) {
self.settings.0.remove(setting);
}
pub fn disable_setting(&mut self, setting: CollectionSetting) {
self.settings.0.insert(setting);
}
}
/// Support for up to 64 user-enabled features on an item.
#[bitflags]
#[repr(u64)]
#[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub enum ItemSetting {
/// This item is transferable.
Transferable,
/// The metadata of this item can be modified.
UnlockedMetadata,
/// Attributes of this item can be modified.
UnlockedAttributes,
}
/// Wrapper type for `BitFlags<ItemSetting>` that implements `Codec`.
#[derive(Clone, Copy, PartialEq, Eq, Default, RuntimeDebug)]
pub struct ItemSettings(pub BitFlags<ItemSetting>);
impl ItemSettings {
pub fn all_enabled() -> Self {
Self(BitFlags::EMPTY)
}
pub fn get_disabled(&self) -> BitFlags<ItemSetting> {
self.0
}
pub fn is_disabled(&self, setting: ItemSetting) -> bool {
self.0.contains(setting)
}
pub fn from_disabled(settings: BitFlags<ItemSetting>) -> Self {
Self(settings)
}
}
impl_codec_bitflags!(ItemSettings, u64, ItemSetting);
/// Item's configuration.
#[derive(
Encode, Decode, Default, PartialEq, RuntimeDebug, Clone, Copy, MaxEncodedLen, TypeInfo,
)]
pub struct ItemConfig {
/// Item's settings.
pub(super) settings: ItemSettings,
}
impl ItemConfig {
pub fn is_setting_enabled(&self, setting: ItemSetting) -> bool {
!self.settings.is_disabled(setting)
}
pub fn has_disabled_setting(&self, setting: ItemSetting) -> bool {
self.settings.is_disabled(setting)
}
pub fn has_disabled_settings(&self) -> bool {
!self.settings.get_disabled().is_empty()
}
pub fn enable_setting(&mut self, setting: ItemSetting) {
self.settings.0.remove(setting);
}
pub fn disable_setting(&mut self, setting: ItemSetting) {
self.settings.0.insert(setting);
}
}
/// Support for up to 64 system-enabled features on a collection.
#[bitflags]
#[repr(u64)]
#[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub enum PalletFeature {
/// Enable/disable trading operations.
Trading,
/// Allow/disallow setting attributes.
Attributes,
/// Allow/disallow transfer approvals.
Approvals,
/// Allow/disallow atomic items swap.
Swaps,
}
/// Wrapper type for `BitFlags<PalletFeature>` that implements `Codec`.
#[derive(Default, RuntimeDebug)]
pub struct PalletFeatures(pub BitFlags<PalletFeature>);
impl PalletFeatures {
pub fn all_enabled() -> Self {
Self(BitFlags::EMPTY)
}
pub fn from_disabled(features: BitFlags<PalletFeature>) -> Self {
Self(features)
}
pub fn is_enabled(&self, feature: PalletFeature) -> bool {
!self.0.contains(feature)
}
}
impl_codec_bitflags!(PalletFeatures, u64, PalletFeature);
/// Support for up to 8 different roles for collections.
#[bitflags]
#[repr(u8)]
#[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub enum CollectionRole {
/// Can mint items.
Issuer,
/// Can freeze items.
Freezer,
/// Can thaw items, force transfers and burn items from any account.
Admin,
}
/// A wrapper type that implements `Codec`.
#[derive(Clone, Copy, PartialEq, Eq, Default, RuntimeDebug)]
pub struct CollectionRoles(pub BitFlags<CollectionRole>);
impl CollectionRoles {
pub fn none() -> Self {
Self(BitFlags::EMPTY)
}
pub fn has_role(&self, role: CollectionRole) -> bool {
self.0.contains(role)
}
pub fn add_role(&mut self, role: CollectionRole) {
self.0.insert(role);
}
pub fn max_roles() -> u8 {
let all: BitFlags<CollectionRole> = BitFlags::all();
all.len() as u8
}
}
impl_codec_bitflags!(CollectionRoles, u8, CollectionRole);
+851
View File
@@ -0,0 +1,851 @@
// This file is part of Substrate.
// Copyright (C) 2022 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_nfts
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// /home/benchbot/cargo_target_dir/production/substrate
// benchmark
// pallet
// --steps=50
// --repeat=20
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json
// --pallet=pallet_nfts
// --chain=dev
// --header=./HEADER-APACHE2
// --output=./frame/nfts/src/weights.rs
// --template=./.maintain/frame-weight-template.hbs
#![cfg_attr(rustfmt, rustfmt_skip)]
#![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_nfts.
pub trait WeightInfo {
fn create() -> Weight;
fn force_create() -> Weight;
fn destroy(n: u32, m: u32, a: u32, ) -> Weight;
fn mint() -> Weight;
fn force_mint() -> Weight;
fn burn() -> Weight;
fn transfer() -> Weight;
fn redeposit(i: u32, ) -> Weight;
fn lock_item_transfer() -> Weight;
fn unlock_item_transfer() -> Weight;
fn lock_collection() -> Weight;
fn transfer_ownership() -> Weight;
fn set_team() -> Weight;
fn force_collection_owner() -> Weight;
fn force_collection_config() -> Weight;
fn lock_item_properties() -> Weight;
fn set_attribute() -> Weight;
fn force_set_attribute() -> Weight;
fn clear_attribute() -> Weight;
fn approve_item_attributes() -> Weight;
fn cancel_item_attributes_approval(n: u32, ) -> Weight;
fn set_metadata() -> Weight;
fn clear_metadata() -> Weight;
fn set_collection_metadata() -> Weight;
fn clear_collection_metadata() -> Weight;
fn approve_transfer() -> Weight;
fn cancel_approval() -> Weight;
fn clear_all_transfer_approvals() -> Weight;
fn set_accept_ownership() -> Weight;
fn set_collection_max_supply() -> Weight;
fn update_mint_settings() -> Weight;
fn set_price() -> Weight;
fn buy_item() -> Weight;
fn pay_tips(n: u32, ) -> Weight;
fn create_swap() -> Weight;
fn cancel_swap() -> Weight;
fn claim_swap() -> Weight;
}
/// Weights for pallet_nfts using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Storage: Nfts NextCollectionId (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:0 w:1)
// Storage: Nfts CollectionConfigOf (r:0 w:1)
// Storage: Nfts CollectionAccount (r:0 w:1)
fn create() -> Weight {
// Minimum execution time: 44_312 nanoseconds.
Weight::from_ref_time(44_871_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(5))
}
// Storage: Nfts NextCollectionId (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:0 w:1)
// Storage: Nfts CollectionConfigOf (r:0 w:1)
// Storage: Nfts CollectionAccount (r:0 w:1)
fn force_create() -> Weight {
// Minimum execution time: 31_654 nanoseconds.
Weight::from_ref_time(32_078_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(5))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts Item (r:1001 w:1000)
// Storage: Nfts Attribute (r:1001 w:1000)
// Storage: Nfts ItemMetadataOf (r:0 w:1000)
// Storage: Nfts CollectionRoleOf (r:0 w:1)
// Storage: Nfts CollectionMetadataOf (r:0 w:1)
// Storage: Nfts CollectionConfigOf (r:0 w:1)
// Storage: Nfts ItemConfigOf (r:0 w:1000)
// Storage: Nfts Account (r:0 w:1000)
// Storage: Nfts CollectionAccount (r:0 w:1)
/// The range of component `n` is `[0, 1000]`.
/// The range of component `m` is `[0, 1000]`.
/// The range of component `a` is `[0, 1000]`.
fn destroy(n: u32, m: u32, a: u32, ) -> Weight {
// Minimum execution time: 19_183_393 nanoseconds.
Weight::from_ref_time(17_061_526_855)
// Standard Error: 16_689
.saturating_add(Weight::from_ref_time(353_523).saturating_mul(n.into()))
// Standard Error: 16_689
.saturating_add(Weight::from_ref_time(1_861_080).saturating_mul(m.into()))
// Standard Error: 16_689
.saturating_add(Weight::from_ref_time(8_858_987).saturating_mul(a.into()))
.saturating_add(T::DbWeight::get().reads(1003))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into())))
.saturating_add(T::DbWeight::get().writes(3005))
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(m.into())))
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into())))
}
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
// Storage: Nfts Account (r:0 w:1)
fn mint() -> Weight {
// Minimum execution time: 57_753 nanoseconds.
Weight::from_ref_time(58_313_000)
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(4))
}
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
// Storage: Nfts Account (r:0 w:1)
fn force_mint() -> Weight {
// Minimum execution time: 56_429 nanoseconds.
Weight::from_ref_time(57_202_000)
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(4))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
// Storage: Nfts Account (r:0 w:1)
// Storage: Nfts ItemPriceOf (r:0 w:1)
// Storage: Nfts ItemAttributesApprovalsOf (r:0 w:1)
// Storage: Nfts PendingSwapOf (r:0 w:1)
fn burn() -> Weight {
// Minimum execution time: 59_681 nanoseconds.
Weight::from_ref_time(60_058_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(7))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: System Account (r:1 w:1)
// Storage: Nfts Account (r:0 w:2)
// Storage: Nfts ItemPriceOf (r:0 w:1)
// Storage: Nfts PendingSwapOf (r:0 w:1)
fn transfer() -> Weight {
// Minimum execution time: 66_085 nanoseconds.
Weight::from_ref_time(67_065_000)
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(6))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts Item (r:102 w:102)
/// The range of component `i` is `[0, 5000]`.
fn redeposit(i: u32, ) -> Weight {
// Minimum execution time: 25_949 nanoseconds.
Weight::from_ref_time(26_106_000)
// Standard Error: 10_326
.saturating_add(Weight::from_ref_time(11_496_776).saturating_mul(i.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into())))
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into())))
}
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
fn lock_item_transfer() -> Weight {
// Minimum execution time: 30_080 nanoseconds.
Weight::from_ref_time(30_825_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
fn unlock_item_transfer() -> Weight {
// Minimum execution time: 30_612 nanoseconds.
Weight::from_ref_time(31_422_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:1)
fn lock_collection() -> Weight {
// Minimum execution time: 27_470 nanoseconds.
Weight::from_ref_time(28_015_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts OwnershipAcceptance (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionAccount (r:0 w:2)
fn transfer_ownership() -> Weight {
// Minimum execution time: 33_750 nanoseconds.
Weight::from_ref_time(34_139_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(4))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:0 w:4)
fn set_team() -> Weight {
// Minimum execution time: 36_565 nanoseconds.
Weight::from_ref_time(37_464_000)
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(5))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionAccount (r:0 w:2)
fn force_collection_owner() -> Weight {
// Minimum execution time: 29_028 nanoseconds.
Weight::from_ref_time(29_479_000)
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(3))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:0 w:1)
fn force_collection_config() -> Weight {
// Minimum execution time: 24_695 nanoseconds.
Weight::from_ref_time(25_304_000)
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
fn lock_item_properties() -> Weight {
// Minimum execution time: 28_910 nanoseconds.
Weight::from_ref_time(29_186_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: Nfts Attribute (r:1 w:1)
fn set_attribute() -> Weight {
// Minimum execution time: 56_407 nanoseconds.
Weight::from_ref_time(58_176_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts Attribute (r:1 w:1)
fn force_set_attribute() -> Weight {
// Minimum execution time: 36_402 nanoseconds.
Weight::from_ref_time(37_034_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: Nfts Attribute (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts ItemConfigOf (r:1 w:0)
fn clear_attribute() -> Weight {
// Minimum execution time: 52_022 nanoseconds.
Weight::from_ref_time(54_059_000)
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: Nfts Item (r:1 w:0)
// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1)
fn approve_item_attributes() -> Weight {
// Minimum execution time: 28_475 nanoseconds.
Weight::from_ref_time(29_162_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:0)
// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1)
// Storage: Nfts Attribute (r:1 w:0)
// Storage: System Account (r:1 w:1)
/// The range of component `n` is `[0, 1000]`.
fn cancel_item_attributes_approval(n: u32, ) -> Weight {
// Minimum execution time: 37_529 nanoseconds.
Weight::from_ref_time(38_023_000)
// Standard Error: 8_136
.saturating_add(Weight::from_ref_time(7_452_872).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(T::DbWeight::get().writes(2))
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into())))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemMetadataOf (r:1 w:1)
fn set_metadata() -> Weight {
// Minimum execution time: 49_300 nanoseconds.
Weight::from_ref_time(49_790_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: Nfts ItemMetadataOf (r:1 w:1)
fn clear_metadata() -> Weight {
// Minimum execution time: 47_248 nanoseconds.
Weight::from_ref_time(48_094_000)
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionMetadataOf (r:1 w:1)
fn set_collection_metadata() -> Weight {
// Minimum execution time: 44_137 nanoseconds.
Weight::from_ref_time(44_905_000)
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts CollectionMetadataOf (r:1 w:1)
fn clear_collection_metadata() -> Weight {
// Minimum execution time: 43_005 nanoseconds.
Weight::from_ref_time(43_898_000)
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
fn approve_transfer() -> Weight {
// Minimum execution time: 36_344 nanoseconds.
Weight::from_ref_time(36_954_000)
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
fn cancel_approval() -> Weight {
// Minimum execution time: 32_418 nanoseconds.
Weight::from_ref_time(33_029_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
fn clear_all_transfer_approvals() -> Weight {
// Minimum execution time: 31_448 nanoseconds.
Weight::from_ref_time(31_979_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts OwnershipAcceptance (r:1 w:1)
fn set_accept_ownership() -> Weight {
// Minimum execution time: 27_487 nanoseconds.
Weight::from_ref_time(28_080_000)
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts CollectionConfigOf (r:1 w:1)
// Storage: Nfts Collection (r:1 w:0)
fn set_collection_max_supply() -> Weight {
// Minimum execution time: 28_235 nanoseconds.
Weight::from_ref_time(28_967_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:1)
fn update_mint_settings() -> Weight {
// Minimum execution time: 28_172 nanoseconds.
Weight::from_ref_time(28_636_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: Nfts ItemPriceOf (r:0 w:1)
fn set_price() -> Weight {
// Minimum execution time: 35_336 nanoseconds.
Weight::from_ref_time(36_026_000)
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts ItemPriceOf (r:1 w:1)
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: System Account (r:1 w:1)
// Storage: Nfts Account (r:0 w:2)
// Storage: Nfts PendingSwapOf (r:0 w:1)
fn buy_item() -> Weight {
// Minimum execution time: 70_971 nanoseconds.
Weight::from_ref_time(72_036_000)
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(6))
}
/// The range of component `n` is `[0, 10]`.
fn pay_tips(n: u32, ) -> Weight {
// Minimum execution time: 5_151 nanoseconds.
Weight::from_ref_time(11_822_888)
// Standard Error: 38_439
.saturating_add(Weight::from_ref_time(3_511_844).saturating_mul(n.into()))
}
// Storage: Nfts Item (r:2 w:0)
// Storage: Nfts PendingSwapOf (r:0 w:1)
fn create_swap() -> Weight {
// Minimum execution time: 33_027 nanoseconds.
Weight::from_ref_time(33_628_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts PendingSwapOf (r:1 w:1)
// Storage: Nfts Item (r:1 w:0)
fn cancel_swap() -> Weight {
// Minimum execution time: 35_890 nanoseconds.
Weight::from_ref_time(36_508_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: Nfts Item (r:2 w:2)
// Storage: Nfts PendingSwapOf (r:1 w:2)
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:2 w:0)
// Storage: System Account (r:1 w:1)
// Storage: Nfts Account (r:0 w:4)
// Storage: Nfts ItemPriceOf (r:0 w:2)
fn claim_swap() -> Weight {
// Minimum execution time: 101_076 nanoseconds.
Weight::from_ref_time(101_863_000)
.saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(11))
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
// Storage: Nfts NextCollectionId (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:0 w:1)
// Storage: Nfts CollectionConfigOf (r:0 w:1)
// Storage: Nfts CollectionAccount (r:0 w:1)
fn create() -> Weight {
// Minimum execution time: 44_312 nanoseconds.
Weight::from_ref_time(44_871_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(5))
}
// Storage: Nfts NextCollectionId (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:0 w:1)
// Storage: Nfts CollectionConfigOf (r:0 w:1)
// Storage: Nfts CollectionAccount (r:0 w:1)
fn force_create() -> Weight {
// Minimum execution time: 31_654 nanoseconds.
Weight::from_ref_time(32_078_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(5))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts Item (r:1001 w:1000)
// Storage: Nfts Attribute (r:1001 w:1000)
// Storage: Nfts ItemMetadataOf (r:0 w:1000)
// Storage: Nfts CollectionRoleOf (r:0 w:1)
// Storage: Nfts CollectionMetadataOf (r:0 w:1)
// Storage: Nfts CollectionConfigOf (r:0 w:1)
// Storage: Nfts ItemConfigOf (r:0 w:1000)
// Storage: Nfts Account (r:0 w:1000)
// Storage: Nfts CollectionAccount (r:0 w:1)
/// The range of component `n` is `[0, 1000]`.
/// The range of component `m` is `[0, 1000]`.
/// The range of component `a` is `[0, 1000]`.
fn destroy(n: u32, m: u32, a: u32, ) -> Weight {
// Minimum execution time: 19_183_393 nanoseconds.
Weight::from_ref_time(17_061_526_855)
// Standard Error: 16_689
.saturating_add(Weight::from_ref_time(353_523).saturating_mul(n.into()))
// Standard Error: 16_689
.saturating_add(Weight::from_ref_time(1_861_080).saturating_mul(m.into()))
// Standard Error: 16_689
.saturating_add(Weight::from_ref_time(8_858_987).saturating_mul(a.into()))
.saturating_add(RocksDbWeight::get().reads(1003))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into())))
.saturating_add(RocksDbWeight::get().writes(3005))
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(m.into())))
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into())))
}
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
// Storage: Nfts Account (r:0 w:1)
fn mint() -> Weight {
// Minimum execution time: 57_753 nanoseconds.
Weight::from_ref_time(58_313_000)
.saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(4))
}
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
// Storage: Nfts Account (r:0 w:1)
fn force_mint() -> Weight {
// Minimum execution time: 56_429 nanoseconds.
Weight::from_ref_time(57_202_000)
.saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(4))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
// Storage: Nfts Account (r:0 w:1)
// Storage: Nfts ItemPriceOf (r:0 w:1)
// Storage: Nfts ItemAttributesApprovalsOf (r:0 w:1)
// Storage: Nfts PendingSwapOf (r:0 w:1)
fn burn() -> Weight {
// Minimum execution time: 59_681 nanoseconds.
Weight::from_ref_time(60_058_000)
.saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(7))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: System Account (r:1 w:1)
// Storage: Nfts Account (r:0 w:2)
// Storage: Nfts ItemPriceOf (r:0 w:1)
// Storage: Nfts PendingSwapOf (r:0 w:1)
fn transfer() -> Weight {
// Minimum execution time: 66_085 nanoseconds.
Weight::from_ref_time(67_065_000)
.saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(6))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts Item (r:102 w:102)
/// The range of component `i` is `[0, 5000]`.
fn redeposit(i: u32, ) -> Weight {
// Minimum execution time: 25_949 nanoseconds.
Weight::from_ref_time(26_106_000)
// Standard Error: 10_326
.saturating_add(Weight::from_ref_time(11_496_776).saturating_mul(i.into()))
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into())))
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into())))
}
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
fn lock_item_transfer() -> Weight {
// Minimum execution time: 30_080 nanoseconds.
Weight::from_ref_time(30_825_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
fn unlock_item_transfer() -> Weight {
// Minimum execution time: 30_612 nanoseconds.
Weight::from_ref_time(31_422_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts CollectionRoleOf (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:1)
fn lock_collection() -> Weight {
// Minimum execution time: 27_470 nanoseconds.
Weight::from_ref_time(28_015_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts OwnershipAcceptance (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionAccount (r:0 w:2)
fn transfer_ownership() -> Weight {
// Minimum execution time: 33_750 nanoseconds.
Weight::from_ref_time(34_139_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(4))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:0 w:4)
fn set_team() -> Weight {
// Minimum execution time: 36_565 nanoseconds.
Weight::from_ref_time(37_464_000)
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(5))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionAccount (r:0 w:2)
fn force_collection_owner() -> Weight {
// Minimum execution time: 29_028 nanoseconds.
Weight::from_ref_time(29_479_000)
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(3))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:0 w:1)
fn force_collection_config() -> Weight {
// Minimum execution time: 24_695 nanoseconds.
Weight::from_ref_time(25_304_000)
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:1)
fn lock_item_properties() -> Weight {
// Minimum execution time: 28_910 nanoseconds.
Weight::from_ref_time(29_186_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: Nfts Attribute (r:1 w:1)
fn set_attribute() -> Weight {
// Minimum execution time: 56_407 nanoseconds.
Weight::from_ref_time(58_176_000)
.saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(2))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts Attribute (r:1 w:1)
fn force_set_attribute() -> Weight {
// Minimum execution time: 36_402 nanoseconds.
Weight::from_ref_time(37_034_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(2))
}
// Storage: Nfts Attribute (r:1 w:1)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts ItemConfigOf (r:1 w:0)
fn clear_attribute() -> Weight {
// Minimum execution time: 52_022 nanoseconds.
Weight::from_ref_time(54_059_000)
.saturating_add(RocksDbWeight::get().reads(3))
.saturating_add(RocksDbWeight::get().writes(2))
}
// Storage: Nfts Item (r:1 w:0)
// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1)
fn approve_item_attributes() -> Weight {
// Minimum execution time: 28_475 nanoseconds.
Weight::from_ref_time(29_162_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:0)
// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1)
// Storage: Nfts Attribute (r:1 w:0)
// Storage: System Account (r:1 w:1)
/// The range of component `n` is `[0, 1000]`.
fn cancel_item_attributes_approval(n: u32, ) -> Weight {
// Minimum execution time: 37_529 nanoseconds.
Weight::from_ref_time(38_023_000)
// Standard Error: 8_136
.saturating_add(Weight::from_ref_time(7_452_872).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(RocksDbWeight::get().writes(2))
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into())))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemMetadataOf (r:1 w:1)
fn set_metadata() -> Weight {
// Minimum execution time: 49_300 nanoseconds.
Weight::from_ref_time(49_790_000)
.saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(2))
}
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: Nfts ItemMetadataOf (r:1 w:1)
fn clear_metadata() -> Weight {
// Minimum execution time: 47_248 nanoseconds.
Weight::from_ref_time(48_094_000)
.saturating_add(RocksDbWeight::get().reads(3))
.saturating_add(RocksDbWeight::get().writes(2))
}
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts Collection (r:1 w:1)
// Storage: Nfts CollectionMetadataOf (r:1 w:1)
fn set_collection_metadata() -> Weight {
// Minimum execution time: 44_137 nanoseconds.
Weight::from_ref_time(44_905_000)
.saturating_add(RocksDbWeight::get().reads(3))
.saturating_add(RocksDbWeight::get().writes(2))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts CollectionMetadataOf (r:1 w:1)
fn clear_collection_metadata() -> Weight {
// Minimum execution time: 43_005 nanoseconds.
Weight::from_ref_time(43_898_000)
.saturating_add(RocksDbWeight::get().reads(3))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
fn approve_transfer() -> Weight {
// Minimum execution time: 36_344 nanoseconds.
Weight::from_ref_time(36_954_000)
.saturating_add(RocksDbWeight::get().reads(3))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
fn cancel_approval() -> Weight {
// Minimum execution time: 32_418 nanoseconds.
Weight::from_ref_time(33_029_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts CollectionRoleOf (r:1 w:0)
fn clear_all_transfer_approvals() -> Weight {
// Minimum execution time: 31_448 nanoseconds.
Weight::from_ref_time(31_979_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts OwnershipAcceptance (r:1 w:1)
fn set_accept_ownership() -> Weight {
// Minimum execution time: 27_487 nanoseconds.
Weight::from_ref_time(28_080_000)
.saturating_add(RocksDbWeight::get().reads(1))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts CollectionConfigOf (r:1 w:1)
// Storage: Nfts Collection (r:1 w:0)
fn set_collection_max_supply() -> Weight {
// Minimum execution time: 28_235 nanoseconds.
Weight::from_ref_time(28_967_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:1)
fn update_mint_settings() -> Weight {
// Minimum execution time: 28_172 nanoseconds.
Weight::from_ref_time(28_636_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: Nfts ItemPriceOf (r:0 w:1)
fn set_price() -> Weight {
// Minimum execution time: 35_336 nanoseconds.
Weight::from_ref_time(36_026_000)
.saturating_add(RocksDbWeight::get().reads(3))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts Item (r:1 w:1)
// Storage: Nfts ItemPriceOf (r:1 w:1)
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:1 w:0)
// Storage: System Account (r:1 w:1)
// Storage: Nfts Account (r:0 w:2)
// Storage: Nfts PendingSwapOf (r:0 w:1)
fn buy_item() -> Weight {
// Minimum execution time: 70_971 nanoseconds.
Weight::from_ref_time(72_036_000)
.saturating_add(RocksDbWeight::get().reads(6))
.saturating_add(RocksDbWeight::get().writes(6))
}
/// The range of component `n` is `[0, 10]`.
fn pay_tips(n: u32, ) -> Weight {
// Minimum execution time: 5_151 nanoseconds.
Weight::from_ref_time(11_822_888)
// Standard Error: 38_439
.saturating_add(Weight::from_ref_time(3_511_844).saturating_mul(n.into()))
}
// Storage: Nfts Item (r:2 w:0)
// Storage: Nfts PendingSwapOf (r:0 w:1)
fn create_swap() -> Weight {
// Minimum execution time: 33_027 nanoseconds.
Weight::from_ref_time(33_628_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts PendingSwapOf (r:1 w:1)
// Storage: Nfts Item (r:1 w:0)
fn cancel_swap() -> Weight {
// Minimum execution time: 35_890 nanoseconds.
Weight::from_ref_time(36_508_000)
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: Nfts Item (r:2 w:2)
// Storage: Nfts PendingSwapOf (r:1 w:2)
// Storage: Nfts Collection (r:1 w:0)
// Storage: Nfts CollectionConfigOf (r:1 w:0)
// Storage: Nfts ItemConfigOf (r:2 w:0)
// Storage: System Account (r:1 w:1)
// Storage: Nfts Account (r:0 w:4)
// Storage: Nfts ItemPriceOf (r:0 w:2)
fn claim_swap() -> Weight {
// Minimum execution time: 101_076 nanoseconds.
Weight::from_ref_time(101_863_000)
.saturating_add(RocksDbWeight::get().reads(8))
.saturating_add(RocksDbWeight::get().writes(11))
}
}