mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 23:31:07 +00:00
NFTs 2.0 (#12765)
* 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:
Generated
+21
-2
@@ -1640,9 +1640,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "enumflags2"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b3ab37dc79652c9d85f1f7b6070d77d321d2467f5fe7b00d6b7a86c57b092ae"
|
||||
checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb"
|
||||
dependencies = [
|
||||
"enumflags2_derive",
|
||||
]
|
||||
@@ -3093,6 +3093,7 @@ dependencies = [
|
||||
"pallet-message-queue",
|
||||
"pallet-mmr",
|
||||
"pallet-multisig",
|
||||
"pallet-nfts",
|
||||
"pallet-nis",
|
||||
"pallet-nomination-pools",
|
||||
"pallet-nomination-pools-benchmarking",
|
||||
@@ -5378,6 +5379,24 @@ dependencies = [
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-nfts"
|
||||
version = "4.0.0-dev"
|
||||
dependencies = [
|
||||
"enumflags2",
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"log",
|
||||
"pallet-balances",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-nicks"
|
||||
version = "4.0.0-dev"
|
||||
|
||||
@@ -122,6 +122,7 @@ members = [
|
||||
"frame/preimage",
|
||||
"frame/proxy",
|
||||
"frame/message-queue",
|
||||
"frame/nfts",
|
||||
"frame/nomination-pools",
|
||||
"frame/nomination-pools/fuzzer",
|
||||
"frame/nomination-pools/benchmarking",
|
||||
|
||||
@@ -78,6 +78,7 @@ pallet-membership = { version = "4.0.0-dev", default-features = false, path = ".
|
||||
pallet-message-queue = { version = "7.0.0-dev", default-features = false, path = "../../../frame/message-queue" }
|
||||
pallet-mmr = { version = "4.0.0-dev", default-features = false, path = "../../../frame/merkle-mountain-range" }
|
||||
pallet-multisig = { version = "4.0.0-dev", default-features = false, path = "../../../frame/multisig" }
|
||||
pallet-nfts = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nfts" }
|
||||
pallet-nomination-pools = { version = "1.0.0", default-features = false, path = "../../../frame/nomination-pools"}
|
||||
pallet-nomination-pools-benchmarking = { version = "1.0.0", default-features = false, optional = true, path = "../../../frame/nomination-pools/benchmarking" }
|
||||
pallet-nomination-pools-runtime-api = { version = "1.0.0-dev", default-features = false, path = "../../../frame/nomination-pools/runtime-api" }
|
||||
@@ -197,6 +198,7 @@ std = [
|
||||
"pallet-root-testing/std",
|
||||
"pallet-recovery/std",
|
||||
"pallet-uniques/std",
|
||||
"pallet-nfts/std",
|
||||
"pallet-vesting/std",
|
||||
"log/std",
|
||||
"frame-try-runtime?/std",
|
||||
@@ -253,6 +255,7 @@ runtime-benchmarks = [
|
||||
"pallet-treasury/runtime-benchmarks",
|
||||
"pallet-utility/runtime-benchmarks",
|
||||
"pallet-uniques/runtime-benchmarks",
|
||||
"pallet-nfts/runtime-benchmarks",
|
||||
"pallet-vesting/runtime-benchmarks",
|
||||
"pallet-whitelist/runtime-benchmarks",
|
||||
"frame-system-benchmarking/runtime-benchmarks",
|
||||
@@ -312,6 +315,7 @@ try-runtime = [
|
||||
"pallet-asset-tx-payment/try-runtime",
|
||||
"pallet-transaction-storage/try-runtime",
|
||||
"pallet-uniques/try-runtime",
|
||||
"pallet-nfts/try-runtime",
|
||||
"pallet-vesting/try-runtime",
|
||||
"pallet-whitelist/try-runtime",
|
||||
]
|
||||
|
||||
@@ -56,6 +56,7 @@ use pallet_grandpa::{
|
||||
fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList,
|
||||
};
|
||||
use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
|
||||
use pallet_nfts::PalletFeatures;
|
||||
use pallet_nis::WithMaximumOf;
|
||||
use pallet_session::historical::{self as pallet_session_historical};
|
||||
pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment};
|
||||
@@ -301,6 +302,7 @@ impl InstanceFilter<RuntimeCall> for ProxyType {
|
||||
RuntimeCall::Balances(..) |
|
||||
RuntimeCall::Assets(..) |
|
||||
RuntimeCall::Uniques(..) |
|
||||
RuntimeCall::Nfts(..) |
|
||||
RuntimeCall::Vesting(pallet_vesting::Call::vested_transfer { .. }) |
|
||||
RuntimeCall::Indices(pallet_indices::Call::transfer { .. })
|
||||
),
|
||||
@@ -1528,6 +1530,10 @@ parameter_types! {
|
||||
pub const ItemDeposit: Balance = 1 * DOLLARS;
|
||||
pub const KeyLimit: u32 = 32;
|
||||
pub const ValueLimit: u32 = 256;
|
||||
pub const ApprovalsLimit: u32 = 20;
|
||||
pub const ItemAttributesApprovalsLimit: u32 = 20;
|
||||
pub const MaxTips: u32 = 10;
|
||||
pub const MaxDeadlineDuration: BlockNumber = 12 * 30 * DAYS;
|
||||
}
|
||||
|
||||
impl pallet_uniques::Config for Runtime {
|
||||
@@ -1551,6 +1557,36 @@ impl pallet_uniques::Config for Runtime {
|
||||
type Locker = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub Features: PalletFeatures = PalletFeatures::all_enabled();
|
||||
}
|
||||
|
||||
impl pallet_nfts::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type CollectionId = u32;
|
||||
type ItemId = u32;
|
||||
type Currency = Balances;
|
||||
type ForceOrigin = frame_system::EnsureRoot<AccountId>;
|
||||
type CollectionDeposit = CollectionDeposit;
|
||||
type ItemDeposit = ItemDeposit;
|
||||
type MetadataDepositBase = MetadataDepositBase;
|
||||
type AttributeDepositBase = MetadataDepositBase;
|
||||
type DepositPerByte = MetadataDepositPerByte;
|
||||
type StringLimit = StringLimit;
|
||||
type KeyLimit = KeyLimit;
|
||||
type ValueLimit = ValueLimit;
|
||||
type ApprovalsLimit = ApprovalsLimit;
|
||||
type ItemAttributesApprovalsLimit = ItemAttributesApprovalsLimit;
|
||||
type MaxTips = MaxTips;
|
||||
type MaxDeadlineDuration = MaxDeadlineDuration;
|
||||
type Features = Features;
|
||||
type WeightInfo = pallet_nfts::weights::SubstrateWeight<Runtime>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type Helper = ();
|
||||
type CreateOrigin = AsEnsureOriginWithArg<EnsureSigned<AccountId>>;
|
||||
type Locker = ();
|
||||
}
|
||||
|
||||
impl pallet_transaction_storage::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Currency = Balances;
|
||||
@@ -1705,6 +1741,7 @@ construct_runtime!(
|
||||
Lottery: pallet_lottery,
|
||||
Nis: pallet_nis,
|
||||
Uniques: pallet_uniques,
|
||||
Nfts: pallet_nfts,
|
||||
TransactionStorage: pallet_transaction_storage,
|
||||
VoterList: pallet_bags_list::<Instance1>,
|
||||
StateTrieMigration: pallet_state_trie_migration,
|
||||
@@ -1836,6 +1873,7 @@ mod benches {
|
||||
[pallet_transaction_storage, TransactionStorage]
|
||||
[pallet_treasury, Treasury]
|
||||
[pallet_uniques, Uniques]
|
||||
[pallet_nfts, Nfts]
|
||||
[pallet_utility, Utility]
|
||||
[pallet_vesting, Vesting]
|
||||
[pallet_whitelist, Whitelist]
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
[package]
|
||||
name = "pallet-nfts"
|
||||
version = "4.0.0-dev"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://substrate.io"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
description = "FRAME NFTs pallet"
|
||||
readme = "README.md"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false }
|
||||
enumflags2 = { version = "0.7.5" }
|
||||
log = { version = "0.4.17", default-features = false }
|
||||
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
|
||||
frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" }
|
||||
frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
|
||||
frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
|
||||
sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" }
|
||||
sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" }
|
||||
|
||||
[dev-dependencies]
|
||||
pallet-balances = { version = "4.0.0-dev", path = "../balances" }
|
||||
sp-core = { version = "7.0.0", path = "../../primitives/core" }
|
||||
sp-io = { version = "7.0.0", path = "../../primitives/io" }
|
||||
sp-std = { version = "5.0.0", path = "../../primitives/std" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"frame-benchmarking?/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"log/std",
|
||||
"scale-info/std",
|
||||
"sp-runtime/std",
|
||||
"sp-std/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = ["frame-support/try-runtime"]
|
||||
@@ -0,0 +1,106 @@
|
||||
# NFTs pallet
|
||||
|
||||
A pallet for dealing with non-fungible assets.
|
||||
|
||||
## Overview
|
||||
|
||||
The NFTs pallet provides functionality for non-fungible tokens' management, including:
|
||||
|
||||
* Collection Creation
|
||||
* NFT Minting
|
||||
* NFT Transfers and Atomic Swaps
|
||||
* NFT Trading methods
|
||||
* Attributes Management
|
||||
* NFT Burning
|
||||
|
||||
To use it in your runtime, you need to implement [`nfts::Config`](https://paritytech.github.io/substrate/master/pallet_nfts/pallet/trait.Config.html).
|
||||
|
||||
The supported dispatchable functions are documented in the [`nfts::Call`](https://paritytech.github.io/substrate/master/pallet_nfts/pallet/enum.Call.html) enum.
|
||||
|
||||
### Terminology
|
||||
|
||||
* **Collection creation:** The creation of a new collection.
|
||||
* **NFT minting:** The action of creating a new item within a collection.
|
||||
* **NFT transfer:** The action of sending an item from one account to another.
|
||||
* **Atomic swap:** The action of exchanging items between accounts without needing a 3rd party service.
|
||||
* **NFT burning:** The destruction of an item.
|
||||
* **Non-fungible token (NFT):** An item for which each unit has unique characteristics. There is exactly
|
||||
one instance of such an item in existence and there is exactly one owning account (though that owning account could be a proxy account or multi-sig account).
|
||||
* **Soul Bound NFT:** An item that is non-transferable from the account which it is minted into.
|
||||
|
||||
### Goals
|
||||
|
||||
The NFTs pallet in Substrate is designed to make the following possible:
|
||||
|
||||
* Allow accounts to permissionlessly create nft collections.
|
||||
* Allow a named (permissioned) account to mint and burn unique items within a collection.
|
||||
* Move items between accounts permissionlessly.
|
||||
* Allow a named (permissioned) account to freeze and unfreeze items within a
|
||||
collection or the entire collection.
|
||||
* Allow the owner of an item to delegate the ability to transfer the item to some
|
||||
named third-party.
|
||||
* Allow third-parties to store information in an NFT _without_ owning it (Eg. save game state).
|
||||
|
||||
## Interface
|
||||
|
||||
### Permissionless dispatchables
|
||||
|
||||
* `create`: Create a new collection by placing a deposit.
|
||||
* `mint`: Mint a new item within a collection (when the minting is public).
|
||||
* `transfer`: Send an item to a new owner.
|
||||
* `redeposit`: Update the deposit amount of an item, potentially freeing funds.
|
||||
* `approve_transfer`: Name a delegate who may authorize a transfer.
|
||||
* `cancel_approval`: Revert the effects of a previous `approve_transfer`.
|
||||
* `approve_item_attributes`: Name a delegate who may change item's attributes within a namespace.
|
||||
* `cancel_item_attributes_approval`: Revert the effects of a previous `approve_item_attributes`.
|
||||
* `set_price`: Set the price for an item.
|
||||
* `buy_item`: Buy an item.
|
||||
* `pay_tips`: Pay tips, could be used for paying the creator royalties.
|
||||
* `create_swap`: Create an offer to swap an NFT for another NFT and optionally some fungibles.
|
||||
* `cancel_swap`: Cancel previously created swap offer.
|
||||
* `claim_swap`: Swap items in an atomic way.
|
||||
|
||||
|
||||
### Permissioned dispatchables
|
||||
|
||||
* `destroy`: Destroy a collection. This destroys all the items inside the collection and refunds the deposit.
|
||||
* `force_mint`: Mint a new item within a collection.
|
||||
* `burn`: Destroy an item within a collection.
|
||||
* `lock_item_transfer`: Prevent an individual item from being transferred.
|
||||
* `unlock_item_transfer`: Revert the effects of a previous `lock_item_transfer`.
|
||||
* `clear_all_transfer_approvals`: Clears all transfer approvals set by calling the `approve_transfer`.
|
||||
* `lock_collection`: Prevent all items within a collection from being transferred (making them all `soul bound`).
|
||||
* `lock_item_properties`: Lock item's metadata or attributes.
|
||||
* `transfer_ownership`: Alter the owner of a collection, moving all associated deposits. (Ownership of individual items will not be affected.)
|
||||
* `set_team`: Alter the permissioned accounts of a collection.
|
||||
* `set_collection_max_supply`: Change the max supply of a collection.
|
||||
* `update_mint_settings`: Update the minting settings for collection.
|
||||
|
||||
|
||||
### Metadata (permissioned) dispatchables
|
||||
|
||||
* `set_attribute`: Set a metadata attribute of an item or collection.
|
||||
* `clear_attribute`: Remove a metadata attribute of an item or collection.
|
||||
* `set_metadata`: Set general metadata of an item (E.g. an IPFS address of an image url).
|
||||
* `clear_metadata`: Remove general metadata of an item.
|
||||
* `set_collection_metadata`: Set general metadata of a collection.
|
||||
* `clear_collection_metadata`: Remove general metadata of a collection.
|
||||
|
||||
|
||||
### Force (i.e. governance) dispatchables
|
||||
|
||||
* `force_create`: Create a new collection (the collection id can not be chosen).
|
||||
* `force_collection_owner`: Change collection's owner.
|
||||
* `force_collection_config`: Change collection's config.
|
||||
* `force_set_attribute`: Set an attribute.
|
||||
|
||||
Please refer to the [`Call`](https://paritytech.github.io/substrate/master/pallet_nfts/pallet/enum.Call.html) enum
|
||||
and its associated variants for documentation on each function.
|
||||
|
||||
## Related Modules
|
||||
|
||||
* [`System`](https://docs.rs/frame-system/latest/frame_system/)
|
||||
* [`Support`](https://docs.rs/frame-support/latest/frame_support/)
|
||||
* [`Assets`](https://docs.rs/pallet-assets/latest/pallet_assets/)
|
||||
|
||||
License: Apache-2.0
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
@@ -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
@@ -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);
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,11 @@ pub mod fungibles;
|
||||
pub mod imbalance;
|
||||
mod misc;
|
||||
pub mod nonfungible;
|
||||
pub mod nonfungible_v2;
|
||||
pub mod nonfungibles;
|
||||
pub mod nonfungibles_v2;
|
||||
pub use imbalance::Imbalance;
|
||||
pub use misc::{
|
||||
AssetId, Balance, BalanceConversion, BalanceStatus, DepositConsequence, ExistenceRequirement,
|
||||
Locker, WithdrawConsequence, WithdrawReasons,
|
||||
AssetId, AttributeNamespace, Balance, BalanceConversion, BalanceStatus, DepositConsequence,
|
||||
ExistenceRequirement, Locker, WithdrawConsequence, WithdrawReasons,
|
||||
};
|
||||
|
||||
@@ -126,6 +126,21 @@ pub enum BalanceStatus {
|
||||
Reserved,
|
||||
}
|
||||
|
||||
/// Attribute namespaces for non-fungible tokens.
|
||||
#[derive(
|
||||
Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen,
|
||||
)]
|
||||
pub enum AttributeNamespace<AccountId> {
|
||||
/// An attribute was set by the pallet.
|
||||
Pallet,
|
||||
/// An attribute was set by collection's owner.
|
||||
CollectionOwner,
|
||||
/// An attribute was set by item's owner.
|
||||
ItemOwner,
|
||||
/// An attribute was set by pre-approved account.
|
||||
Account(AccountId),
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Reasons for moving funds out of an account.
|
||||
#[derive(Encode, Decode, MaxEncodedLen)]
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
// 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.
|
||||
|
||||
//! Traits for dealing with a single non-fungible item.
|
||||
//!
|
||||
//! This assumes a single-level namespace identified by `Inspect::ItemId`, and could
|
||||
//! reasonably be implemented by pallets that want to expose a single collection of NFT-like
|
||||
//! objects.
|
||||
//!
|
||||
//! For an NFT API that has dual-level namespacing, the traits in `nonfungibles` are better to
|
||||
//! use.
|
||||
|
||||
use super::nonfungibles_v2 as nonfungibles;
|
||||
use crate::{
|
||||
dispatch::DispatchResult,
|
||||
traits::{tokens::misc::AttributeNamespace, Get},
|
||||
};
|
||||
use codec::{Decode, Encode};
|
||||
use sp_runtime::TokenError;
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// Trait for providing an interface to a read-only NFT-like item.
|
||||
pub trait Inspect<AccountId> {
|
||||
/// Type for identifying an item.
|
||||
type ItemId;
|
||||
|
||||
/// Returns the owner of `item`, or `None` if the item doesn't exist or has no
|
||||
/// owner.
|
||||
fn owner(item: &Self::ItemId) -> Option<AccountId>;
|
||||
|
||||
/// Returns the attribute value of `item` corresponding to `key`.
|
||||
///
|
||||
/// By default this is `None`; no attributes are defined.
|
||||
fn attribute(
|
||||
_item: &Self::ItemId,
|
||||
_namespace: &AttributeNamespace<AccountId>,
|
||||
_key: &[u8],
|
||||
) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the strongly-typed attribute value of `item` corresponding to `key`.
|
||||
///
|
||||
/// By default this just attempts to use `attribute`.
|
||||
fn typed_attribute<K: Encode, V: Decode>(
|
||||
item: &Self::ItemId,
|
||||
namespace: &AttributeNamespace<AccountId>,
|
||||
key: &K,
|
||||
) -> Option<V> {
|
||||
key.using_encoded(|d| Self::attribute(item, namespace, d))
|
||||
.and_then(|v| V::decode(&mut &v[..]).ok())
|
||||
}
|
||||
|
||||
/// Returns `true` if the `item` may be transferred.
|
||||
///
|
||||
/// Default implementation is that all items are transferable.
|
||||
fn can_transfer(_item: &Self::ItemId) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface for enumerating items in existence or owned by a given account over a collection
|
||||
/// of NFTs.
|
||||
pub trait InspectEnumerable<AccountId>: Inspect<AccountId> {
|
||||
/// The iterator type for [`Self::items`].
|
||||
type ItemsIterator: Iterator<Item = Self::ItemId>;
|
||||
/// The iterator type for [`Self::owned`].
|
||||
type OwnedIterator: Iterator<Item = Self::ItemId>;
|
||||
|
||||
/// Returns an iterator of the items within a `collection` in existence.
|
||||
fn items() -> Self::ItemsIterator;
|
||||
|
||||
/// Returns an iterator of the items of all collections owned by `who`.
|
||||
fn owned(who: &AccountId) -> Self::OwnedIterator;
|
||||
}
|
||||
|
||||
/// Trait for providing an interface for NFT-like items which may be minted, burned and/or have
|
||||
/// attributes set on them.
|
||||
pub trait Mutate<AccountId, ItemConfig>: Inspect<AccountId> {
|
||||
/// Mint some `item` to be owned by `who`.
|
||||
///
|
||||
/// By default, this is not a supported operation.
|
||||
fn mint_into(
|
||||
_item: &Self::ItemId,
|
||||
_who: &AccountId,
|
||||
_config: &ItemConfig,
|
||||
_deposit_collection_owner: bool,
|
||||
) -> DispatchResult {
|
||||
Err(TokenError::Unsupported.into())
|
||||
}
|
||||
|
||||
/// Burn some `item`.
|
||||
///
|
||||
/// By default, this is not a supported operation.
|
||||
fn burn(_item: &Self::ItemId, _maybe_check_owner: Option<&AccountId>) -> DispatchResult {
|
||||
Err(TokenError::Unsupported.into())
|
||||
}
|
||||
|
||||
/// Set attribute `value` of `item`'s `key`.
|
||||
///
|
||||
/// By default, this is not a supported operation.
|
||||
fn set_attribute(_item: &Self::ItemId, _key: &[u8], _value: &[u8]) -> DispatchResult {
|
||||
Err(TokenError::Unsupported.into())
|
||||
}
|
||||
|
||||
/// Attempt to set the strongly-typed attribute `value` of `item`'s `key`.
|
||||
///
|
||||
/// By default this just attempts to use `set_attribute`.
|
||||
fn set_typed_attribute<K: Encode, V: Encode>(
|
||||
item: &Self::ItemId,
|
||||
key: &K,
|
||||
value: &V,
|
||||
) -> DispatchResult {
|
||||
key.using_encoded(|k| value.using_encoded(|v| Self::set_attribute(item, k, v)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for transferring a non-fungible item.
|
||||
pub trait Transfer<AccountId>: Inspect<AccountId> {
|
||||
/// Transfer `item` into `destination` account.
|
||||
fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult;
|
||||
}
|
||||
|
||||
/// Convert a `nonfungibles` trait implementation into a `nonfungible` trait implementation by
|
||||
/// identifying a single item.
|
||||
pub struct ItemOf<
|
||||
F: nonfungibles::Inspect<AccountId>,
|
||||
A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
|
||||
AccountId,
|
||||
>(sp_std::marker::PhantomData<(F, A, AccountId)>);
|
||||
|
||||
impl<
|
||||
F: nonfungibles::Inspect<AccountId>,
|
||||
A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
|
||||
AccountId,
|
||||
> Inspect<AccountId> for ItemOf<F, A, AccountId>
|
||||
{
|
||||
type ItemId = <F as nonfungibles::Inspect<AccountId>>::ItemId;
|
||||
fn owner(item: &Self::ItemId) -> Option<AccountId> {
|
||||
<F as nonfungibles::Inspect<AccountId>>::owner(&A::get(), item)
|
||||
}
|
||||
fn attribute(
|
||||
item: &Self::ItemId,
|
||||
namespace: &AttributeNamespace<AccountId>,
|
||||
key: &[u8],
|
||||
) -> Option<Vec<u8>> {
|
||||
<F as nonfungibles::Inspect<AccountId>>::attribute(&A::get(), item, namespace, key)
|
||||
}
|
||||
fn typed_attribute<K: Encode, V: Decode>(
|
||||
item: &Self::ItemId,
|
||||
namespace: &AttributeNamespace<AccountId>,
|
||||
key: &K,
|
||||
) -> Option<V> {
|
||||
<F as nonfungibles::Inspect<AccountId>>::typed_attribute(&A::get(), item, namespace, key)
|
||||
}
|
||||
fn can_transfer(item: &Self::ItemId) -> bool {
|
||||
<F as nonfungibles::Inspect<AccountId>>::can_transfer(&A::get(), item)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
F: nonfungibles::InspectEnumerable<AccountId>,
|
||||
A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
|
||||
AccountId,
|
||||
> InspectEnumerable<AccountId> for ItemOf<F, A, AccountId>
|
||||
{
|
||||
type ItemsIterator = <F as nonfungibles::InspectEnumerable<AccountId>>::ItemsIterator;
|
||||
type OwnedIterator =
|
||||
<F as nonfungibles::InspectEnumerable<AccountId>>::OwnedInCollectionIterator;
|
||||
|
||||
fn items() -> Self::ItemsIterator {
|
||||
<F as nonfungibles::InspectEnumerable<AccountId>>::items(&A::get())
|
||||
}
|
||||
fn owned(who: &AccountId) -> Self::OwnedIterator {
|
||||
<F as nonfungibles::InspectEnumerable<AccountId>>::owned_in_collection(&A::get(), who)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
F: nonfungibles::Mutate<AccountId, ItemConfig>,
|
||||
A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
|
||||
AccountId,
|
||||
ItemConfig,
|
||||
> Mutate<AccountId, ItemConfig> for ItemOf<F, A, AccountId>
|
||||
{
|
||||
fn mint_into(
|
||||
item: &Self::ItemId,
|
||||
who: &AccountId,
|
||||
config: &ItemConfig,
|
||||
deposit_collection_owner: bool,
|
||||
) -> DispatchResult {
|
||||
<F as nonfungibles::Mutate<AccountId, ItemConfig>>::mint_into(
|
||||
&A::get(),
|
||||
item,
|
||||
who,
|
||||
config,
|
||||
deposit_collection_owner,
|
||||
)
|
||||
}
|
||||
fn burn(item: &Self::ItemId, maybe_check_owner: Option<&AccountId>) -> DispatchResult {
|
||||
<F as nonfungibles::Mutate<AccountId, ItemConfig>>::burn(&A::get(), item, maybe_check_owner)
|
||||
}
|
||||
fn set_attribute(item: &Self::ItemId, key: &[u8], value: &[u8]) -> DispatchResult {
|
||||
<F as nonfungibles::Mutate<AccountId, ItemConfig>>::set_attribute(
|
||||
&A::get(),
|
||||
item,
|
||||
key,
|
||||
value,
|
||||
)
|
||||
}
|
||||
fn set_typed_attribute<K: Encode, V: Encode>(
|
||||
item: &Self::ItemId,
|
||||
key: &K,
|
||||
value: &V,
|
||||
) -> DispatchResult {
|
||||
<F as nonfungibles::Mutate<AccountId, ItemConfig>>::set_typed_attribute(
|
||||
&A::get(),
|
||||
item,
|
||||
key,
|
||||
value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
F: nonfungibles::Transfer<AccountId>,
|
||||
A: Get<<F as nonfungibles::Inspect<AccountId>>::CollectionId>,
|
||||
AccountId,
|
||||
> Transfer<AccountId> for ItemOf<F, A, AccountId>
|
||||
{
|
||||
fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult {
|
||||
<F as nonfungibles::Transfer<AccountId>>::transfer(&A::get(), item, destination)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
// 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.
|
||||
|
||||
//! Traits for dealing with multiple collections of non-fungible items.
|
||||
//!
|
||||
//! This assumes a dual-level namespace identified by `Inspect::ItemId`, and could
|
||||
//! reasonably be implemented by pallets which want to expose multiple independent collections of
|
||||
//! NFT-like objects.
|
||||
//!
|
||||
//! For an NFT API which has single-level namespacing, the traits in `nonfungible` are better to
|
||||
//! use.
|
||||
//!
|
||||
//! Implementations of these traits may be converted to implementations of corresponding
|
||||
//! `nonfungible` traits by using the `nonfungible::ItemOf` type adapter.
|
||||
|
||||
use crate::{
|
||||
dispatch::{DispatchError, DispatchResult},
|
||||
traits::tokens::misc::AttributeNamespace,
|
||||
};
|
||||
use codec::{Decode, Encode};
|
||||
use sp_runtime::TokenError;
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// Trait for providing an interface to many read-only NFT-like sets of items.
|
||||
pub trait Inspect<AccountId> {
|
||||
/// Type for identifying an item.
|
||||
type ItemId;
|
||||
|
||||
/// Type for identifying a collection (an identifier for an independent collection of
|
||||
/// items).
|
||||
type CollectionId;
|
||||
|
||||
/// Returns the owner of `item` of `collection`, or `None` if the item doesn't exist
|
||||
/// (or somehow has no owner).
|
||||
fn owner(collection: &Self::CollectionId, item: &Self::ItemId) -> Option<AccountId>;
|
||||
|
||||
/// Returns the owner of the `collection`, if there is one. For many NFTs this may not
|
||||
/// make any sense, so users of this API should not be surprised to find a collection
|
||||
/// results in `None` here.
|
||||
fn collection_owner(_collection: &Self::CollectionId) -> Option<AccountId> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the attribute value of `item` of `collection` corresponding to `key`.
|
||||
///
|
||||
/// By default this is `None`; no attributes are defined.
|
||||
fn attribute(
|
||||
_collection: &Self::CollectionId,
|
||||
_item: &Self::ItemId,
|
||||
_namespace: &AttributeNamespace<AccountId>,
|
||||
_key: &[u8],
|
||||
) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the strongly-typed attribute value of `item` of `collection` corresponding to
|
||||
/// `key`.
|
||||
///
|
||||
/// By default this just attempts to use `attribute`.
|
||||
fn typed_attribute<K: Encode, V: Decode>(
|
||||
collection: &Self::CollectionId,
|
||||
item: &Self::ItemId,
|
||||
namespace: &AttributeNamespace<AccountId>,
|
||||
key: &K,
|
||||
) -> Option<V> {
|
||||
key.using_encoded(|d| Self::attribute(collection, item, namespace, d))
|
||||
.and_then(|v| V::decode(&mut &v[..]).ok())
|
||||
}
|
||||
|
||||
/// Returns the attribute value of `collection` corresponding to `key`.
|
||||
///
|
||||
/// By default this is `None`; no attributes are defined.
|
||||
fn collection_attribute(_collection: &Self::CollectionId, _key: &[u8]) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the strongly-typed attribute value of `collection` corresponding to `key`.
|
||||
///
|
||||
/// By default this just attempts to use `collection_attribute`.
|
||||
fn typed_collection_attribute<K: Encode, V: Decode>(
|
||||
collection: &Self::CollectionId,
|
||||
key: &K,
|
||||
) -> Option<V> {
|
||||
key.using_encoded(|d| Self::collection_attribute(collection, d))
|
||||
.and_then(|v| V::decode(&mut &v[..]).ok())
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface for enumerating items in existence or owned by a given account over many collections
|
||||
/// of NFTs.
|
||||
pub trait InspectEnumerable<AccountId>: Inspect<AccountId> {
|
||||
/// The iterator type for [`Self::collections`].
|
||||
type CollectionsIterator: Iterator<Item = Self::CollectionId>;
|
||||
/// The iterator type for [`Self::items`].
|
||||
type ItemsIterator: Iterator<Item = Self::ItemId>;
|
||||
/// The iterator type for [`Self::owned`].
|
||||
type OwnedIterator: Iterator<Item = (Self::CollectionId, Self::ItemId)>;
|
||||
/// The iterator type for [`Self::owned_in_collection`].
|
||||
type OwnedInCollectionIterator: Iterator<Item = Self::ItemId>;
|
||||
|
||||
/// Returns an iterator of the collections in existence.
|
||||
fn collections() -> Self::CollectionsIterator;
|
||||
|
||||
/// Returns an iterator of the items of a `collection` in existence.
|
||||
fn items(collection: &Self::CollectionId) -> Self::ItemsIterator;
|
||||
|
||||
/// Returns an iterator of the items of all collections owned by `who`.
|
||||
fn owned(who: &AccountId) -> Self::OwnedIterator;
|
||||
|
||||
/// Returns an iterator of the items of `collection` owned by `who`.
|
||||
fn owned_in_collection(
|
||||
collection: &Self::CollectionId,
|
||||
who: &AccountId,
|
||||
) -> Self::OwnedInCollectionIterator;
|
||||
}
|
||||
|
||||
/// Trait for providing the ability to create collections of nonfungible items.
|
||||
pub trait Create<AccountId, CollectionConfig>: Inspect<AccountId> {
|
||||
/// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`.
|
||||
fn create_collection(
|
||||
who: &AccountId,
|
||||
admin: &AccountId,
|
||||
config: &CollectionConfig,
|
||||
) -> Result<Self::CollectionId, DispatchError>;
|
||||
}
|
||||
|
||||
/// Trait for providing the ability to destroy collections of nonfungible items.
|
||||
pub trait Destroy<AccountId>: Inspect<AccountId> {
|
||||
/// The witness data needed to destroy an item.
|
||||
type DestroyWitness;
|
||||
|
||||
/// Provide the appropriate witness data needed to destroy an item.
|
||||
fn get_destroy_witness(collection: &Self::CollectionId) -> Option<Self::DestroyWitness>;
|
||||
|
||||
/// Destroy an existing fungible item.
|
||||
/// * `collection`: The `CollectionId` to be destroyed.
|
||||
/// * `witness`: Any witness data that needs to be provided to complete the operation
|
||||
/// successfully.
|
||||
/// * `maybe_check_owner`: An optional `AccountId` that can be used to authorize the destroy
|
||||
/// command. If not provided, we will not do any authorization checks before destroying the
|
||||
/// item.
|
||||
///
|
||||
/// If successful, this function will return the actual witness data from the destroyed item.
|
||||
/// This may be different than the witness data provided, and can be used to refund weight.
|
||||
fn destroy(
|
||||
collection: Self::CollectionId,
|
||||
witness: Self::DestroyWitness,
|
||||
maybe_check_owner: Option<AccountId>,
|
||||
) -> Result<Self::DestroyWitness, DispatchError>;
|
||||
}
|
||||
|
||||
/// Trait for providing an interface for multiple collections of NFT-like items which may be
|
||||
/// minted, burned and/or have attributes set on them.
|
||||
pub trait Mutate<AccountId, ItemConfig>: Inspect<AccountId> {
|
||||
/// Mint some `item` of `collection` to be owned by `who`.
|
||||
///
|
||||
/// By default, this is not a supported operation.
|
||||
fn mint_into(
|
||||
_collection: &Self::CollectionId,
|
||||
_item: &Self::ItemId,
|
||||
_who: &AccountId,
|
||||
_config: &ItemConfig,
|
||||
_deposit_collection_owner: bool,
|
||||
) -> DispatchResult {
|
||||
Err(TokenError::Unsupported.into())
|
||||
}
|
||||
|
||||
/// Burn some `item` of `collection`.
|
||||
///
|
||||
/// By default, this is not a supported operation.
|
||||
fn burn(
|
||||
_collection: &Self::CollectionId,
|
||||
_item: &Self::ItemId,
|
||||
_maybe_check_owner: Option<&AccountId>,
|
||||
) -> DispatchResult {
|
||||
Err(TokenError::Unsupported.into())
|
||||
}
|
||||
|
||||
/// Set attribute `value` of `item` of `collection`'s `key`.
|
||||
///
|
||||
/// By default, this is not a supported operation.
|
||||
fn set_attribute(
|
||||
_collection: &Self::CollectionId,
|
||||
_item: &Self::ItemId,
|
||||
_key: &[u8],
|
||||
_value: &[u8],
|
||||
) -> DispatchResult {
|
||||
Err(TokenError::Unsupported.into())
|
||||
}
|
||||
|
||||
/// Attempt to set the strongly-typed attribute `value` of `item` of `collection`'s `key`.
|
||||
///
|
||||
/// By default this just attempts to use `set_attribute`.
|
||||
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::set_attribute(collection, item, k, v)))
|
||||
}
|
||||
|
||||
/// Set attribute `value` of `collection`'s `key`.
|
||||
///
|
||||
/// By default, this is not a supported operation.
|
||||
fn set_collection_attribute(
|
||||
_collection: &Self::CollectionId,
|
||||
_key: &[u8],
|
||||
_value: &[u8],
|
||||
) -> DispatchResult {
|
||||
Err(TokenError::Unsupported.into())
|
||||
}
|
||||
|
||||
/// Attempt to set the strongly-typed attribute `value` of `collection`'s `key`.
|
||||
///
|
||||
/// By default this just attempts to use `set_attribute`.
|
||||
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::set_collection_attribute(collection, k, v))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for transferring non-fungible sets of items.
|
||||
pub trait Transfer<AccountId>: Inspect<AccountId> {
|
||||
/// Transfer `item` of `collection` into `destination` account.
|
||||
fn transfer(
|
||||
collection: &Self::CollectionId,
|
||||
item: &Self::ItemId,
|
||||
destination: &AccountId,
|
||||
) -> DispatchResult;
|
||||
}
|
||||
+1
-1
@@ -26,5 +26,5 @@ error[E0277]: the trait bound `Vec<u8>: MaxEncodedLen` is not satisfied
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5)
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6)
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7)
|
||||
and 78 others
|
||||
and 79 others
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageMyStorage<T>, Vec<u8>>` to implement `StorageInfoTrait`
|
||||
|
||||
+3
-3
@@ -28,7 +28,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
|
||||
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
|
||||
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
|
||||
<&[T] as EncodeLike<Vec<U>>>
|
||||
and 280 others
|
||||
and 281 others
|
||||
= note: required for `Bar` to implement `FullEncode`
|
||||
= note: required for `Bar` to implement `FullCodec`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `PartialStorageInfoTrait`
|
||||
@@ -69,7 +69,7 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied
|
||||
(A, B, C, D)
|
||||
(A, B, C, D, E)
|
||||
(A, B, C, D, E, F)
|
||||
and 162 others
|
||||
and 163 others
|
||||
= note: required for `Bar` to implement `StaticTypeInfo`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
|
||||
|
||||
@@ -103,7 +103,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
|
||||
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
|
||||
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
|
||||
<&[T] as EncodeLike<Vec<U>>>
|
||||
and 280 others
|
||||
and 281 others
|
||||
= note: required for `Bar` to implement `FullEncode`
|
||||
= note: required for `Bar` to implement `FullCodec`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
|
||||
|
||||
+3
-3
@@ -28,7 +28,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
|
||||
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
|
||||
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
|
||||
<&[T] as EncodeLike<Vec<U>>>
|
||||
and 280 others
|
||||
and 281 others
|
||||
= note: required for `Bar` to implement `FullEncode`
|
||||
= note: required for `Bar` to implement `FullCodec`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `PartialStorageInfoTrait`
|
||||
@@ -69,7 +69,7 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied
|
||||
(A, B, C, D)
|
||||
(A, B, C, D, E)
|
||||
(A, B, C, D, E, F)
|
||||
and 162 others
|
||||
and 163 others
|
||||
= note: required for `Bar` to implement `StaticTypeInfo`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
|
||||
|
||||
@@ -103,7 +103,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
|
||||
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
|
||||
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
|
||||
<&[T] as EncodeLike<Vec<U>>>
|
||||
and 280 others
|
||||
and 281 others
|
||||
= note: required for `Bar` to implement `FullEncode`
|
||||
= note: required for `Bar` to implement `FullCodec`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
|
||||
|
||||
@@ -13,5 +13,5 @@ error[E0277]: the trait bound `Bar: MaxEncodedLen` is not satisfied
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5)
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6)
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7)
|
||||
and 78 others
|
||||
and 79 others
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageInfoTrait`
|
||||
|
||||
@@ -13,6 +13,6 @@ error[E0277]: the trait bound `Bar: MaxEncodedLen` is not satisfied
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5)
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6)
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7)
|
||||
and 78 others
|
||||
and 79 others
|
||||
= note: required for `Key<frame_support::Twox64Concat, Bar>` to implement `KeyGeneratorMaxEncodedLen`
|
||||
= note: required for `frame_support::pallet_prelude::StorageNMap<_GeneratedPrefixForStorageFoo<T>, Key<frame_support::Twox64Concat, Bar>, u32>` to implement `StorageInfoTrait`
|
||||
|
||||
Reference in New Issue
Block a user