difftreelog
refactor move collection dispatch to runtime
in: master
13 files changed
pallets/common/src/dispatch.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/common/src/dispatch.rs
@@ -0,0 +1,68 @@
+use frame_support::{
+ dispatch::{
+ DispatchResultWithPostInfo, PostDispatchInfo, Weight, DispatchErrorWithPostInfo,
+ DispatchResult,
+ },
+ weights::Pays,
+ traits::Get,
+};
+use up_data_structs::{CollectionId, CreateCollectionData};
+
+use crate::{pallet::Config, CommonCollectionOperations, CollectionHandle};
+
+// TODO: move to benchmarking
+/// Price of [`dispatch_call`] call with noop `call` argument
+pub fn dispatch_weight<T: Config>() -> Weight {
+ // Read collection
+ <T as frame_system::Config>::DbWeight::get().reads(1)
+ // Dynamic dispatch?
+ + 6_000_000
+ // submit_logs is measured as part of collection pallets
+}
+
+/// Helper function to implement substrate calls for common collection methods
+pub fn dispatch_call<
+ T: Config,
+ C: FnOnce(&dyn CommonCollectionOperations<T>) -> DispatchResultWithPostInfo,
+>(
+ collection: CollectionId,
+ call: C,
+) -> DispatchResultWithPostInfo {
+ let handle =
+ CollectionHandle::try_get(collection).map_err(|error| DispatchErrorWithPostInfo {
+ post_info: PostDispatchInfo {
+ actual_weight: Some(dispatch_weight::<T>()),
+ pays_fee: Pays::Yes,
+ },
+ error,
+ })?;
+ let dispatched = T::CollectionDispatch::dispatch(handle);
+ let mut result = call(dispatched.as_dyn());
+ match &mut result {
+ Ok(PostDispatchInfo {
+ actual_weight: Some(weight),
+ ..
+ })
+ | Err(DispatchErrorWithPostInfo {
+ post_info: PostDispatchInfo {
+ actual_weight: Some(weight),
+ ..
+ },
+ ..
+ }) => *weight += dispatch_weight::<T>(),
+ _ => {}
+ }
+
+ dispatched.into_inner().submit_logs();
+ result
+}
+
+pub trait CollectionDispatch<T: Config> {
+ fn create(sender: T::AccountId, data: CreateCollectionData<T::AccountId>) -> DispatchResult;
+ fn destroy(sender: T::CrossAccountId, handle: CollectionHandle<T>) -> DispatchResult;
+
+ fn dispatch(handle: CollectionHandle<T>) -> Self;
+ fn into_inner(self) -> CollectionHandle<T>;
+
+ fn as_dyn(&self) -> &dyn CommonCollectionOperations<T>;
+}
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -21,17 +21,17 @@
use sp_std::vec::Vec;
use pallet_evm::account::CrossAccountId;
use frame_support::{
- dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo},
+ dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},
ensure, fail,
- traits::{Imbalance, Get, Currency},
+ traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},
BoundedVec,
+ weights::Pays,
};
use pallet_evm::GasWeightMapping;
use up_data_structs::{
- COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,
- MAX_TOKEN_PREFIX_LENGTH, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo,
- TokenId, Weight, WithdrawReasons, CollectionStats, MAX_TOKEN_OWNERSHIP, CollectionMode,
- NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, MAX_TOKEN_PREFIX_LENGTH,
+ COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, TokenId, CollectionStats, MAX_TOKEN_OWNERSHIP,
+ CollectionMode, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,
CustomDataLimit, CreateCollectionData, SponsorshipState, CreateItemExData, SponsoringRateLimit,
};
@@ -40,6 +40,7 @@
use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;
+pub mod dispatch;
pub mod erc;
pub mod eth;
@@ -163,9 +164,11 @@
use super::*;
use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key};
use pallet_evm::account;
+ use dispatch::CollectionDispatch;
use frame_support::traits::Currency;
use up_data_structs::TokenId;
use scale_info::TypeInfo;
+ use up_evm_mapping::CrossAccountId;
#[pallet::config]
pub trait Config:
@@ -179,6 +182,7 @@
type CollectionCreationPrice: Get<
<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,
>;
+ type CollectionDispatch: CollectionDispatch<Self>;
type TreasuryAccountId: Get<Self::AccountId>;
}
pallets/unique/src/common.rsdiffbeforeafterboth--- a/pallets/unique/src/common.rs
+++ b/pallets/unique/src/common.rs
@@ -16,14 +16,14 @@
use core::marker::PhantomData;
use frame_support::{weights::Weight};
-use pallet_common::{CommonWeightInfo};
+use pallet_common::{CommonWeightInfo, dispatch::dispatch_weight};
use pallet_fungible::{common::CommonWeights as FungibleWeights};
use pallet_nonfungible::{common::CommonWeights as NonfungibleWeights};
use pallet_refungible::{common::CommonWeights as RefungibleWeights};
use up_data_structs::CreateItemExData;
-use crate::{Config, dispatch::dispatch_weight};
+use crate::Config;
macro_rules! max_weight_of {
($method:ident ( $($args:tt)* )) => {
@@ -35,7 +35,7 @@
pub struct CommonWeights<T: Config>(PhantomData<T>);
impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
- fn create_item() -> up_data_structs::Weight {
+ fn create_item() -> Weight {
dispatch_weight::<T>() + max_weight_of!(create_item())
}
@@ -51,19 +51,19 @@
dispatch_weight::<T>() + max_weight_of!(burn_item())
}
- fn transfer() -> up_data_structs::Weight {
+ fn transfer() -> Weight {
dispatch_weight::<T>() + max_weight_of!(transfer())
}
- fn approve() -> up_data_structs::Weight {
+ fn approve() -> Weight {
dispatch_weight::<T>() + max_weight_of!(approve())
}
- fn transfer_from() -> up_data_structs::Weight {
+ fn transfer_from() -> Weight {
dispatch_weight::<T>() + max_weight_of!(transfer_from())
}
- fn set_variable_metadata(bytes: u32) -> up_data_structs::Weight {
+ fn set_variable_metadata(bytes: u32) -> Weight {
dispatch_weight::<T>() + max_weight_of!(set_variable_metadata(bytes))
}
pallets/unique/src/dispatch.rsdiffbeforeafterboth--- a/pallets/unique/src/dispatch.rs
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-use frame_support::{
- dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo},
- traits::Get,
- weights::Weight,
-};
-use up_data_structs::{CollectionId, CollectionMode, Pays, PostDispatchInfo};
-use pallet_common::{CollectionHandle, CommonCollectionOperations};
-use pallet_fungible::FungibleHandle;
-use pallet_nonfungible::NonfungibleHandle;
-use pallet_refungible::RefungibleHandle;
-
-use crate::Config;
-
-// TODO: move to benchmarking
-/// Price of [`dispatch_call`] call with noop `call` argument
-pub fn dispatch_weight<T: Config>() -> Weight {
- // Read collection
- <T as frame_system::Config>::DbWeight::get().reads(1)
- // Dynamic dispatch?
- + 6_000_000
- // submit_logs is measured as part of collection pallets
-}
-
-pub enum Dispatched<T: Config> {
- Fungible(FungibleHandle<T>),
- Nonfungible(NonfungibleHandle<T>),
- Refungible(RefungibleHandle<T>),
-}
-impl<T: Config> Dispatched<T> {
- pub fn dispatch(handle: CollectionHandle<T>) -> Self {
- match handle.mode {
- CollectionMode::Fungible(_) => Self::Fungible(FungibleHandle::cast(handle)),
- CollectionMode::NFT => Self::Nonfungible(NonfungibleHandle::cast(handle)),
- CollectionMode::ReFungible => Self::Refungible(RefungibleHandle::cast(handle)),
- }
- }
- fn into_inner(self) -> CollectionHandle<T> {
- match self {
- Dispatched::Fungible(f) => f.into_inner(),
- Dispatched::Nonfungible(f) => f.into_inner(),
- Dispatched::Refungible(f) => f.into_inner(),
- }
- }
- pub fn as_dyn(&self) -> &dyn CommonCollectionOperations<T> {
- match self {
- Dispatched::Fungible(h) => h,
- Dispatched::Nonfungible(h) => h,
- Dispatched::Refungible(h) => h,
- }
- }
-}
-
-/// Helper function to implement substrate calls for common collection methods
-pub fn dispatch_call<
- T: Config,
- C: FnOnce(&dyn pallet_common::CommonCollectionOperations<T>) -> DispatchResultWithPostInfo,
->(
- collection: CollectionId,
- call: C,
-) -> DispatchResultWithPostInfo {
- let handle =
- CollectionHandle::try_get(collection).map_err(|error| DispatchErrorWithPostInfo {
- post_info: PostDispatchInfo {
- actual_weight: Some(dispatch_weight::<T>()),
- pays_fee: Pays::Yes,
- },
- error,
- })?;
- let dispatched = Dispatched::dispatch(handle);
- let mut result = call(dispatched.as_dyn());
- match &mut result {
- Ok(PostDispatchInfo {
- actual_weight: Some(weight),
- ..
- })
- | Err(DispatchErrorWithPostInfo {
- post_info: PostDispatchInfo {
- actual_weight: Some(weight),
- ..
- },
- ..
- }) => *weight += dispatch_weight::<T>(),
- _ => {}
- }
-
- dispatched.into_inner().submit_logs();
- result
-}
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -15,82 +15,3 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
pub mod sponsoring;
-
-use fp_evm::PrecompileResult;
-use pallet_common::{
- CollectionById,
- erc::CommonEvmHandler,
- eth::{map_eth_to_id, map_eth_to_token_id},
-};
-use pallet_fungible::FungibleHandle;
-use pallet_nonfungible::NonfungibleHandle;
-use pallet_refungible::{RefungibleHandle, erc::RefungibleTokenHandle};
-use sp_std::borrow::ToOwned;
-use sp_std::vec::Vec;
-use sp_core::{H160, U256};
-use crate::{CollectionMode, Config, dispatch::Dispatched};
-use pallet_common::CollectionHandle;
-
-pub struct UniqueErcSupport<T: Config>(core::marker::PhantomData<T>);
-
-impl<T: Config> pallet_evm::OnMethodCall<T> for UniqueErcSupport<T> {
- fn is_reserved(target: &H160) -> bool {
- map_eth_to_id(target).is_some()
- }
- fn is_used(target: &H160) -> bool {
- map_eth_to_id(target)
- .map(<CollectionById<T>>::contains_key)
- .unwrap_or(false)
- }
- fn get_code(target: &H160) -> Option<Vec<u8>> {
- if let Some(collection_id) = map_eth_to_id(target) {
- let collection = <CollectionById<T>>::get(collection_id)?;
- Some(
- match collection.mode {
- CollectionMode::NFT => <NonfungibleHandle<T>>::CODE,
- CollectionMode::Fungible(_) => <FungibleHandle<T>>::CODE,
- CollectionMode::ReFungible => <RefungibleHandle<T>>::CODE,
- }
- .to_owned(),
- )
- } else if let Some((collection_id, _token_id)) = map_eth_to_token_id(target) {
- let collection = <CollectionById<T>>::get(collection_id)?;
- if collection.mode != CollectionMode::ReFungible {
- return None;
- }
- // TODO: check token existence
- Some(<RefungibleTokenHandle<T>>::CODE.to_owned())
- } else {
- None
- }
- }
- fn call(
- source: &H160,
- target: &H160,
- gas_limit: u64,
- input: &[u8],
- value: U256,
- ) -> Option<PrecompileResult> {
- if let Some(collection_id) = map_eth_to_id(target) {
- let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;
- let dispatched = Dispatched::dispatch(collection);
-
- match dispatched {
- Dispatched::Fungible(h) => h.call(source, input, value),
- Dispatched::Nonfungible(h) => h.call(source, input, value),
- Dispatched::Refungible(h) => h.call(source, input, value),
- }
- } else if let Some((collection_id, token_id)) = map_eth_to_token_id(target) {
- let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;
- if collection.mode != CollectionMode::ReFungible {
- return None;
- }
-
- let handle = RefungibleHandle::cast(collection);
- // TODO: check token existence
- RefungibleTokenHandle(handle, token_id).call(source, input, value)
- } else {
- None
- }
- }
-}
pallets/unique/src/eth/sponsoring.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/sponsoring.rs
+++ b/pallets/unique/src/eth/sponsoring.rs
@@ -25,6 +25,7 @@
use core::marker::PhantomData;
use core::convert::TryInto;
use pallet_evm::account::CrossAccountId;
+use up_data_structs::{TokenId, CreateItemData, CreateNftData};
use pallet_nonfungible::erc::{
UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call,
pallets/unique/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![recursion_limit = "1024"]18#![cfg_attr(not(feature = "std"), no_std)]19#![allow(20 clippy::too_many_arguments,21 clippy::unnecessary_mut_passed,22 clippy::unused_unit23)]2425extern crate alloc;2627pub use serde::{Serialize, Deserialize};2829pub use frame_support::{30 construct_runtime, decl_module, decl_storage, decl_error, decl_event,31 dispatch::DispatchResult,32 ensure, fail, parameter_types,33 traits::{34 ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness,35 IsSubType, WithdrawReasons,36 },37 weights::{38 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},39 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,40 WeightToFeePolynomial, DispatchClass,41 },42 StorageValue, transactional,43 pallet_prelude::{DispatchResultWithPostInfo, ConstU32},44 BoundedVec,45};46use scale_info::TypeInfo;47use frame_system::{self as system, ensure_signed};48use sp_runtime::{sp_std::prelude::Vec};49use up_data_structs::{50 MAX_DECIMAL_POINTS, VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT,51 OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,52 MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData, CollectionLimits, CollectionId,53 CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,54 CreateCollectionData, CustomDataLimit, CreateItemExData,55};56use pallet_common::{CollectionHandle, Pallet as PalletCommon, Error as CommonError, CommonWeightInfo};57use pallet_evm::account::CrossAccountId;58use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};59use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};60use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};6162#[cfg(test)]63mod mock;6465#[cfg(test)]66mod tests;6768mod eth;69mod sponsorship;70pub use sponsorship::{UniqueSponsorshipHandler, UniqueSponsorshipPredict};71pub use eth::sponsoring::UniqueEthSponsorshipHandler;7273pub use eth::UniqueErcSupport;7475pub mod common;76use common::CommonWeights;77pub mod dispatch;78use dispatch::dispatch_call;7980#[cfg(feature = "runtime-benchmarks")]81mod benchmarking;82pub mod weights;83use weights::WeightInfo;8485pub trait SponsorshipPredict<T: Config> {86 fn predict(collection: CollectionId, account: T::CrossAccountId, token: TokenId) -> Option<u64>87 where88 u64: From<<T as frame_system::Config>::BlockNumber>;89}9091decl_error! {92 /// Error for non-fungible-token module.93 pub enum Error for Module<T: Config> {94 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.95 CollectionDecimalPointLimitExceeded,96 /// This address is not set as sponsor, use setCollectionSponsor first.97 ConfirmUnsetSponsorFail,98 /// Length of items properties must be greater than 0.99 EmptyArgument,100 }101}102103pub trait Config:104 system::Config105 + pallet_evm_coder_substrate::Config106 + pallet_common::Config107 + pallet_nonfungible::Config108 + pallet_refungible::Config109 + pallet_fungible::Config110 + Sized111 + TypeInfo112{113 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;114115 /// Weight information for extrinsics in this pallet.116 type WeightInfo: WeightInfo;117}118119decl_event! {120 pub enum Event<T>121 where122 <T as frame_system::Config>::AccountId,123 <T as pallet_evm::account::Config>::CrossAccountId,124 {125 /// Collection sponsor was removed126 ///127 /// # Arguments128 ///129 /// * collection_id: Globally unique collection identifier.130 CollectionSponsorRemoved(CollectionId),131132 /// Collection admin was added133 ///134 /// # Arguments135 ///136 /// * collection_id: Globally unique collection identifier.137 ///138 /// * admin: Admin address.139 CollectionAdminAdded(CollectionId, CrossAccountId),140141 /// Collection owned was change142 ///143 /// # Arguments144 ///145 /// * collection_id: Globally unique collection identifier.146 ///147 /// * owner: New owner address.148 CollectionOwnedChanged(CollectionId, AccountId),149150 /// Collection sponsor was set151 ///152 /// # Arguments153 ///154 /// * collection_id: Globally unique collection identifier.155 ///156 /// * owner: New sponsor address.157 CollectionSponsorSet(CollectionId, AccountId),158159 /// const on chain schema was set160 ///161 /// # Arguments162 ///163 /// * collection_id: Globally unique collection identifier.164 ConstOnChainSchemaSet(CollectionId),165166 /// New sponsor was confirm167 ///168 /// # Arguments169 ///170 /// * collection_id: Globally unique collection identifier.171 ///172 /// * sponsor: New sponsor address.173 SponsorshipConfirmed(CollectionId, AccountId),174175 /// Collection admin was removed176 ///177 /// # Arguments178 ///179 /// * collection_id: Globally unique collection identifier.180 ///181 /// * admin: Admin address.182 CollectionAdminRemoved(CollectionId, CrossAccountId),183184 /// Address was remove from allow list185 ///186 /// # Arguments187 ///188 /// * collection_id: Globally unique collection identifier.189 ///190 /// * user: Address.191 AllowListAddressRemoved(CollectionId, CrossAccountId),192193 /// Address was add to allow list194 ///195 /// # Arguments196 ///197 /// * collection_id: Globally unique collection identifier.198 ///199 /// * user: Address.200 AllowListAddressAdded(CollectionId, CrossAccountId),201202 /// Collection limits was set203 ///204 /// # Arguments205 ///206 /// * collection_id: Globally unique collection identifier.207 CollectionLimitSet(CollectionId),208209 /// Mint permission was set210 ///211 /// # Arguments212 ///213 /// * collection_id: Globally unique collection identifier.214 MintPermissionSet(CollectionId),215216 /// Offchain schema was set217 ///218 /// # Arguments219 ///220 /// * collection_id: Globally unique collection identifier.221 OffchainSchemaSet(CollectionId),222223 /// Public access mode was set224 ///225 /// # Arguments226 ///227 /// * collection_id: Globally unique collection identifier.228 ///229 /// * mode: New access state.230 PublicAccessModeSet(CollectionId, AccessMode),231232 /// Schema version was set233 ///234 /// # Arguments235 ///236 /// * collection_id: Globally unique collection identifier.237 SchemaVersionSet(CollectionId),238239 /// Variable on chain schema was set240 ///241 /// # Arguments242 ///243 /// * collection_id: Globally unique collection identifier.244 VariableOnChainSchemaSet(CollectionId),245 }246}247248type SelfWeightOf<T> = <T as Config>::WeightInfo;249250// # Used definitions251//252// ## User control levels253//254// chain-controlled - key is uncontrolled by user255// i.e autoincrementing index256// can use non-cryptographic hash257// real - key is controlled by user258// but it is hard to generate enough colliding values, i.e owner of signed txs259// can use non-cryptographic hash260// controlled - key is completly controlled by users261// i.e maps with mutable keys262// should use cryptographic hash263//264// ## User control level downgrade reasons265//266// ?1 - chain-controlled -> controlled267// collections/tokens can be destroyed, resulting in massive holes268// ?2 - chain-controlled -> controlled269// same as ?1, but can be only added, resulting in easier exploitation270// ?3 - real -> controlled271// no confirmation required, so addresses can be easily generated272decl_storage! {273 trait Store for Module<T: Config> as Unique {274275 //#region Private members276 /// Used for migrations277 ChainVersion: u64;278 //#endregion279280 //#region Tokens transfer rate limit baskets281 /// (Collection id (controlled?2), who created (real))282 /// TODO: Off chain worker should remove from this map when collection gets removed283 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;284 /// Collection id (controlled?2), token id (controlled?2)285 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;286 /// Collection id (controlled?2), owning user (real)287 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;288 /// Collection id (controlled?2), token id (controlled?2)289 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;290 //#endregion291292 /// Variable metadata sponsoring293 /// Collection id (controlled?2), token id (controlled?2)294 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;295 /// Approval sponsoring296 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;297 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;298 pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;299 }300}301302decl_module! {303 pub struct Module<T: Config> for enum Call304 where305 origin: T::Origin306 {307 type Error = Error<T>;308309 fn deposit_event() = default;310311 fn on_initialize(_now: T::BlockNumber) -> Weight {312 0313 }314315 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.316 ///317 /// # Permissions318 ///319 /// * Anyone.320 ///321 /// # Arguments322 ///323 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.324 ///325 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.326 ///327 /// * token_prefix: UTF-8 string with token prefix.328 ///329 /// * mode: [CollectionMode] collection type and type dependent data.330 // returns collection ID331 #[weight = <SelfWeightOf<T>>::create_collection()]332 #[transactional]333 #[deprecated]334 pub fn create_collection(origin,335 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,336 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,337 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,338 mode: CollectionMode) -> DispatchResult {339 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {340 name: collection_name,341 description: collection_description,342 token_prefix,343 mode,344 ..Default::default()345 };346 Self::create_collection_ex(origin, data)347 }348349 /// This method creates a collection350 ///351 /// Prefer it to deprecated [`created_collection`] method352 #[weight = <SelfWeightOf<T>>::create_collection()]353 #[transactional]354 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {355 let owner = ensure_signed(origin)?;356357 let _id = match data.mode {358 CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(owner, data)?},359 CollectionMode::Fungible(decimal_points) => {360 // check params361 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);362 <PalletFungible<T>>::init_collection(owner, data)?363 }364 CollectionMode::ReFungible => {365 <PalletRefungible<T>>::init_collection(owner, data)?366 }367 };368369 Ok(())370 }371372 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.373 ///374 /// # Permissions375 ///376 /// * Collection Owner.377 ///378 /// # Arguments379 ///380 /// * collection_id: collection to destroy.381 #[weight = <SelfWeightOf<T>>::destroy_collection()]382 #[transactional]383 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {384 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);385386 let collection = <CollectionHandle<T>>::try_get(collection_id)?;387 collection.check_is_owner(&sender)?;388389 // =========390391 match collection.mode {392 CollectionMode::ReFungible => PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?,393 CollectionMode::Fungible(_) => PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?,394 CollectionMode::NFT => PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?,395 }396397 <NftTransferBasket<T>>::remove_prefix(collection_id, None);398 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);399 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);400401 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);402 <NftApproveBasket<T>>::remove_prefix(collection_id, None);403 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);404 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);405406 Ok(())407 }408409 /// Add an address to allow list.410 ///411 /// # Permissions412 ///413 /// * Collection Owner414 /// * Collection Admin415 ///416 /// # Arguments417 ///418 /// * collection_id.419 ///420 /// * address.421 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]422 #[transactional]423 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{424425 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);426 let collection = <CollectionHandle<T>>::try_get(collection_id)?;427428 <PalletCommon<T>>::toggle_allowlist(429 &collection,430 &sender,431 &address,432 true,433 )?;434435 Self::deposit_event(Event::<T>::AllowListAddressAdded(436 collection_id,437 address438 ));439440 Ok(())441 }442443 /// Remove an address from allow list.444 ///445 /// # Permissions446 ///447 /// * Collection Owner448 /// * Collection Admin449 ///450 /// # Arguments451 ///452 /// * collection_id.453 ///454 /// * address.455 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]456 #[transactional]457 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{458459 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);460 let collection = <CollectionHandle<T>>::try_get(collection_id)?;461462 <PalletCommon<T>>::toggle_allowlist(463 &collection,464 &sender,465 &address,466 false,467 )?;468469 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(470 collection_id,471 address472 ));473474 Ok(())475 }476477 /// Toggle between normal and allow list access for the methods with access for `Anyone`.478 ///479 /// # Permissions480 ///481 /// * Collection Owner.482 ///483 /// # Arguments484 ///485 /// * collection_id.486 ///487 /// * mode: [AccessMode]488 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]489 #[transactional]490 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult491 {492 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);493494 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;495 target_collection.check_is_owner(&sender)?;496497 target_collection.access = mode.clone();498499 <Pallet<T>>::deposit_event(Event::<T>::PublicAccessModeSet(500 collection_id,501 mode502 ));503504 target_collection.save()505 }506507 /// Allows Anyone to create tokens if:508 /// * Allow List is enabled, and509 /// * Address is added to allow list, and510 /// * This method was called with True parameter511 ///512 /// # Permissions513 /// * Collection Owner514 ///515 /// # Arguments516 ///517 /// * collection_id.518 ///519 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.520 #[weight = <SelfWeightOf<T>>::set_mint_permission()]521 #[transactional]522 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult523 {524 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);525526 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;527 target_collection.check_is_owner(&sender)?;528529 target_collection.mint_mode = mint_permission;530531 <Pallet<T>>::deposit_event(Event::<T>::MintPermissionSet(532 collection_id533 ));534535 target_collection.save()536 }537538 /// Change the owner of the collection.539 ///540 /// # Permissions541 ///542 /// * Collection Owner.543 ///544 /// # Arguments545 ///546 /// * collection_id.547 ///548 /// * new_owner.549 #[weight = <SelfWeightOf<T>>::change_collection_owner()]550 #[transactional]551 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {552553 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);554555 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;556 target_collection.check_is_owner(&sender)?;557558 target_collection.owner = new_owner.clone();559 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(560 collection_id,561 new_owner562 ));563564 target_collection.save()565 }566567 /// Adds an admin of the Collection.568 /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.569 ///570 /// # Permissions571 ///572 /// * Collection Owner.573 /// * Collection Admin.574 ///575 /// # Arguments576 ///577 /// * collection_id: ID of the Collection to add admin for.578 ///579 /// * new_admin_id: Address of new admin to add.580 #[weight = <SelfWeightOf<T>>::add_collection_admin()]581 #[transactional]582 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {583 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);584 let collection = <CollectionHandle<T>>::try_get(collection_id)?;585586 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(587 collection_id,588 new_admin_id.clone()589 ));590591 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)592 }593594 /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.595 ///596 /// # Permissions597 ///598 /// * Collection Owner.599 /// * Collection Admin.600 ///601 /// # Arguments602 ///603 /// * collection_id: ID of the Collection to remove admin for.604 ///605 /// * account_id: Address of admin to remove.606 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]607 #[transactional]608 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {609 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);610 let collection = <CollectionHandle<T>>::try_get(collection_id)?;611612 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(613 collection_id,614 account_id.clone()615 ));616617 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)618 }619620 /// # Permissions621 ///622 /// * Collection Owner623 ///624 /// # Arguments625 ///626 /// * collection_id.627 ///628 /// * new_sponsor.629 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]630 #[transactional]631 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {632 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);633634 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;635 target_collection.check_is_owner(&sender)?;636637 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());638639 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(640 collection_id,641 new_sponsor642 ));643644 target_collection.save()645 }646647 /// # Permissions648 ///649 /// * Sponsor.650 ///651 /// # Arguments652 ///653 /// * collection_id.654 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]655 #[transactional]656 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {657 let sender = ensure_signed(origin)?;658659 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;660 ensure!(661 target_collection.sponsorship.pending_sponsor() == Some(&sender),662 Error::<T>::ConfirmUnsetSponsorFail663 );664665 target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());666667 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(668 collection_id,669 sender670 ));671672 target_collection.save()673 }674675 /// Switch back to pay-per-own-transaction model.676 ///677 /// # Permissions678 ///679 /// * Collection owner.680 ///681 /// # Arguments682 ///683 /// * collection_id.684 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]685 #[transactional]686 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {687 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);688689 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;690 target_collection.check_is_owner(&sender)?;691692 target_collection.sponsorship = SponsorshipState::Disabled;693694 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(695 collection_id696 ));697 target_collection.save()698 }699700 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.701 ///702 /// # Permissions703 ///704 /// * Collection Owner.705 /// * Collection Admin.706 /// * Anyone if707 /// * Allow List is enabled, and708 /// * Address is added to allow list, and709 /// * MintPermission is enabled (see SetMintPermission method)710 ///711 /// # Arguments712 ///713 /// * collection_id: ID of the collection.714 ///715 /// * owner: Address, initial owner of the NFT.716 ///717 /// * data: Token data to store on chain.718 #[weight = <CommonWeights<T>>::create_item()]719 #[transactional]720 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {721 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);722723 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data))724 }725726 /// This method creates multiple items in a collection created with CreateCollection method.727 ///728 /// # Permissions729 ///730 /// * Collection Owner.731 /// * Collection Admin.732 /// * Anyone if733 /// * Allow List is enabled, and734 /// * Address is added to allow list, and735 /// * MintPermission is enabled (see SetMintPermission method)736 ///737 /// # Arguments738 ///739 /// * collection_id: ID of the collection.740 ///741 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].742 ///743 /// * owner: Address, initial owner of the NFT.744 #[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]745 #[transactional]746 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {747 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);748 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);749750 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))751 }752753 #[weight = <CommonWeights<T>>::create_multiple_items_ex(&data)]754 #[transactional]755 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {756 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);757758 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data))759 }760761 // TODO! transaction weight762763 /// Set transfers_enabled value for particular collection764 ///765 /// # Permissions766 ///767 /// * Collection Owner.768 ///769 /// # Arguments770 ///771 /// * collection_id: ID of the collection.772 ///773 /// * value: New flag value.774 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]775 #[transactional]776 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {777 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);778 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;779 target_collection.check_is_owner(&sender)?;780781 // =========782783 target_collection.limits.transfers_enabled = Some(value);784 target_collection.save()785 }786787 /// Destroys a concrete instance of NFT.788 ///789 /// # Permissions790 ///791 /// * Collection Owner.792 /// * Collection Admin.793 /// * Current NFT Owner.794 ///795 /// # Arguments796 ///797 /// * collection_id: ID of the collection.798 ///799 /// * item_id: ID of NFT to burn.800 #[weight = <CommonWeights<T>>::burn_item()]801 #[transactional]802 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {803 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);804805 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;806 if value == 1 {807 <NftTransferBasket<T>>::remove(collection_id, item_id);808 <NftApproveBasket<T>>::remove(collection_id, item_id);809 }810 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?811 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());812 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));813 Ok(post_info)814 }815816 /// Destroys a concrete instance of NFT on behalf of the owner817 /// See also: [`approve`]818 ///819 /// # Permissions820 ///821 /// * Collection Owner.822 /// * Collection Admin.823 /// * Current NFT Owner.824 ///825 /// # Arguments826 ///827 /// * collection_id: ID of the collection.828 ///829 /// * item_id: ID of NFT to burn.830 ///831 /// * from: owner of item832 #[weight = <CommonWeights<T>>::burn_from()]833 #[transactional]834 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {835 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);836837 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))838 }839840 /// Change ownership of the token.841 ///842 /// # Permissions843 ///844 /// * Collection Owner845 /// * Collection Admin846 /// * Current NFT owner847 ///848 /// # Arguments849 ///850 /// * recipient: Address of token recipient.851 ///852 /// * collection_id.853 ///854 /// * item_id: ID of the item855 /// * Non-Fungible Mode: Required.856 /// * Fungible Mode: Ignored.857 /// * Re-Fungible Mode: Required.858 ///859 /// * value: Amount to transfer.860 /// * Non-Fungible Mode: Ignored861 /// * Fungible Mode: Must specify transferred amount862 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)863 #[weight = <CommonWeights<T>>::transfer()]864 #[transactional]865 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {866 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);867868 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value))869 }870871 /// Set, change, or remove approved address to transfer the ownership of the NFT.872 ///873 /// # Permissions874 ///875 /// * Collection Owner876 /// * Collection Admin877 /// * Current NFT owner878 ///879 /// # Arguments880 ///881 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).882 ///883 /// * collection_id.884 ///885 /// * item_id: ID of the item.886 #[weight = <CommonWeights<T>>::approve()]887 #[transactional]888 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {889 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);890891 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))892 }893894 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.895 ///896 /// # Permissions897 /// * Collection Owner898 /// * Collection Admin899 /// * Current NFT owner900 /// * Address approved by current NFT owner901 ///902 /// # Arguments903 ///904 /// * from: Address that owns token.905 ///906 /// * recipient: Address of token recipient.907 ///908 /// * collection_id.909 ///910 /// * item_id: ID of the item.911 ///912 /// * value: Amount to transfer.913 #[weight = <CommonWeights<T>>::transfer_from()]914 #[transactional]915 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {916 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);917918 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value))919 }920921 /// Set off-chain data schema.922 ///923 /// # Permissions924 ///925 /// * Collection Owner926 /// * Collection Admin927 ///928 /// # Arguments929 ///930 /// * collection_id.931 ///932 /// * schema: String representing the offchain data schema.933 #[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]934 #[transactional]935 pub fn set_variable_meta_data (936 origin,937 collection_id: CollectionId,938 item_id: TokenId,939 data: BoundedVec<u8, CustomDataLimit>,940 ) -> DispatchResultWithPostInfo {941 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);942943 dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))944 }945946 /// Set meta_update_permission value for particular collection947 ///948 /// # Permissions949 ///950 /// * Collection Owner.951 ///952 /// # Arguments953 ///954 /// * collection_id: ID of the collection.955 ///956 /// * value: New flag value.957 #[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]958 #[transactional]959 pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {960 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);961 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;962963 ensure!(964 target_collection.meta_update_permission != MetaUpdatePermission::None,965 <CommonError<T>>::MetadataFlagFrozen,966 );967 target_collection.check_is_owner(&sender)?;968969 target_collection.meta_update_permission = value;970971 target_collection.save()972 }973974 /// Set schema standard975 /// ImageURL976 /// Unique977 ///978 /// # Permissions979 ///980 /// * Collection Owner981 /// * Collection Admin982 ///983 /// # Arguments984 ///985 /// * collection_id.986 ///987 /// * schema: SchemaVersion: enum988 #[weight = <SelfWeightOf<T>>::set_schema_version()]989 #[transactional]990 pub fn set_schema_version(991 origin,992 collection_id: CollectionId,993 version: SchemaVersion994 ) -> DispatchResult {995 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);996 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;997 target_collection.check_is_owner_or_admin(&sender)?;998 target_collection.schema_version = version;9991000 <Pallet<T>>::deposit_event(Event::<T>::SchemaVersionSet(1001 collection_id1002 ));10031004 target_collection.save()1005 }10061007 /// Set off-chain data schema.1008 ///1009 /// # Permissions1010 ///1011 /// * Collection Owner1012 /// * Collection Admin1013 ///1014 /// # Arguments1015 ///1016 /// * collection_id.1017 ///1018 /// * schema: String representing the offchain data schema.1019 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1020 #[transactional]1021 pub fn set_offchain_schema(1022 origin,1023 collection_id: CollectionId,1024 schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,1025 ) -> DispatchResult {1026 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1027 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1028 target_collection.check_is_owner_or_admin(&sender)?;10291030 target_collection.offchain_schema = schema;10311032 <Pallet<T>>::deposit_event(Event::<T>::OffchainSchemaSet(1033 collection_id1034 ));10351036 target_collection.save()1037 }10381039 /// Set const on-chain data schema.1040 ///1041 /// # Permissions1042 ///1043 /// * Collection Owner1044 /// * Collection Admin1045 ///1046 /// # Arguments1047 ///1048 /// * collection_id.1049 ///1050 /// * schema: String representing the const on-chain data schema.1051 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1052 #[transactional]1053 pub fn set_const_on_chain_schema (1054 origin,1055 collection_id: CollectionId,1056 schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>1057 ) -> DispatchResult {1058 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1059 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1060 target_collection.check_is_owner_or_admin(&sender)?;10611062 target_collection.const_on_chain_schema = schema;10631064 <Pallet<T>>::deposit_event(Event::<T>::ConstOnChainSchemaSet(1065 collection_id1066 ));10671068 target_collection.save()1069 }10701071 /// Set variable on-chain data schema.1072 ///1073 /// # Permissions1074 ///1075 /// * Collection Owner1076 /// * Collection Admin1077 ///1078 /// # Arguments1079 ///1080 /// * collection_id.1081 ///1082 /// * schema: String representing the variable on-chain data schema.1083 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1084 #[transactional]1085 pub fn set_variable_on_chain_schema (1086 origin,1087 collection_id: CollectionId,1088 schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>1089 ) -> DispatchResult {1090 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1091 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1092 target_collection.check_is_owner_or_admin(&sender)?;10931094 target_collection.variable_on_chain_schema = schema;10951096 <Pallet<T>>::deposit_event(Event::<T>::VariableOnChainSchemaSet(1097 collection_id1098 ));10991100 target_collection.save()1101 }11021103 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1104 #[transactional]1105 pub fn set_collection_limits(1106 origin,1107 collection_id: CollectionId,1108 new_limit: CollectionLimits,1109 ) -> DispatchResult {1110 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1111 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1112 target_collection.check_is_owner(&sender)?;1113 let old_limit = &target_collection.limits;11141115 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;11161117 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1118 collection_id1119 ));11201121 target_collection.save()1122 }1123 }1124}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![recursion_limit = "1024"]18#![cfg_attr(not(feature = "std"), no_std)]19#![allow(20 clippy::too_many_arguments,21 clippy::unnecessary_mut_passed,22 clippy::unused_unit23)]2425extern crate alloc;2627pub use serde::{Serialize, Deserialize};2829use frame_support::{30 decl_module, decl_storage, decl_error, decl_event,31 dispatch::DispatchResult,32 ensure,33 weights::{Weight},34 transactional,35 pallet_prelude::{DispatchResultWithPostInfo, ConstU32},36 BoundedVec,37};38use scale_info::TypeInfo;39use frame_system::{self as system, ensure_signed};40use sp_runtime::{sp_std::prelude::Vec};41use up_data_structs::{42 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT,43 MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,44 AccessMode, CreateItemData, CollectionLimits, CollectionId, CollectionMode, TokenId,45 SchemaVersion, SponsorshipState, MetaUpdatePermission, CreateCollectionData, CustomDataLimit,46 CreateItemExData,47};48use pallet_evm::account::CrossAccountId;49use pallet_common::{50 CollectionHandle, Pallet as PalletCommon, Error as CommonError, CommonWeightInfo,51 dispatch::dispatch_call, dispatch::CollectionDispatch,52};5354#[cfg(test)]55mod mock;5657#[cfg(test)]58mod tests;5960mod eth;61mod sponsorship;62pub use sponsorship::{UniqueSponsorshipHandler, UniqueSponsorshipPredict};63pub use eth::sponsoring::UniqueEthSponsorshipHandler;6465pub mod common;66use common::CommonWeights;6768#[cfg(feature = "runtime-benchmarks")]69mod benchmarking;70pub mod weights;71use weights::WeightInfo;7273pub trait SponsorshipPredict<T: Config> {74 fn predict(collection: CollectionId, account: T::CrossAccountId, token: TokenId) -> Option<u64>75 where76 u64: From<<T as frame_system::Config>::BlockNumber>;77}7879decl_error! {80 /// Error for non-fungible-token module.81 pub enum Error for Module<T: Config> {82 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.83 CollectionDecimalPointLimitExceeded,84 /// This address is not set as sponsor, use setCollectionSponsor first.85 ConfirmUnsetSponsorFail,86 /// Length of items properties must be greater than 0.87 EmptyArgument,88 }89}9091pub trait Config:92 system::Config93 + pallet_evm_coder_substrate::Config94 + pallet_common::Config95 + pallet_nonfungible::Config96 + pallet_refungible::Config97 + pallet_fungible::Config98 + Sized99 + TypeInfo100{101 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;102103 /// Weight information for extrinsics in this pallet.104 type WeightInfo: WeightInfo;105}106107decl_event! {108 pub enum Event<T>109 where110 <T as frame_system::Config>::AccountId,111 <T as pallet_evm::account::Config>::CrossAccountId,112 {113 /// Collection sponsor was removed114 ///115 /// # Arguments116 ///117 /// * collection_id: Globally unique collection identifier.118 CollectionSponsorRemoved(CollectionId),119120 /// Collection admin was added121 ///122 /// # Arguments123 ///124 /// * collection_id: Globally unique collection identifier.125 ///126 /// * admin: Admin address.127 CollectionAdminAdded(CollectionId, CrossAccountId),128129 /// Collection owned was change130 ///131 /// # Arguments132 ///133 /// * collection_id: Globally unique collection identifier.134 ///135 /// * owner: New owner address.136 CollectionOwnedChanged(CollectionId, AccountId),137138 /// Collection sponsor was set139 ///140 /// # Arguments141 ///142 /// * collection_id: Globally unique collection identifier.143 ///144 /// * owner: New sponsor address.145 CollectionSponsorSet(CollectionId, AccountId),146147 /// const on chain schema was set148 ///149 /// # Arguments150 ///151 /// * collection_id: Globally unique collection identifier.152 ConstOnChainSchemaSet(CollectionId),153154 /// New sponsor was confirm155 ///156 /// # Arguments157 ///158 /// * collection_id: Globally unique collection identifier.159 ///160 /// * sponsor: New sponsor address.161 SponsorshipConfirmed(CollectionId, AccountId),162163 /// Collection admin was removed164 ///165 /// # Arguments166 ///167 /// * collection_id: Globally unique collection identifier.168 ///169 /// * admin: Admin address.170 CollectionAdminRemoved(CollectionId, CrossAccountId),171172 /// Address was remove from allow list173 ///174 /// # Arguments175 ///176 /// * collection_id: Globally unique collection identifier.177 ///178 /// * user: Address.179 AllowListAddressRemoved(CollectionId, CrossAccountId),180181 /// Address was add to allow list182 ///183 /// # Arguments184 ///185 /// * collection_id: Globally unique collection identifier.186 ///187 /// * user: Address.188 AllowListAddressAdded(CollectionId, CrossAccountId),189190 /// Collection limits was set191 ///192 /// # Arguments193 ///194 /// * collection_id: Globally unique collection identifier.195 CollectionLimitSet(CollectionId),196197 /// Mint permission was set198 ///199 /// # Arguments200 ///201 /// * collection_id: Globally unique collection identifier.202 MintPermissionSet(CollectionId),203204 /// Offchain schema was set205 ///206 /// # Arguments207 ///208 /// * collection_id: Globally unique collection identifier.209 OffchainSchemaSet(CollectionId),210211 /// Public access mode was set212 ///213 /// # Arguments214 ///215 /// * collection_id: Globally unique collection identifier.216 ///217 /// * mode: New access state.218 PublicAccessModeSet(CollectionId, AccessMode),219220 /// Schema version was set221 ///222 /// # Arguments223 ///224 /// * collection_id: Globally unique collection identifier.225 SchemaVersionSet(CollectionId),226227 /// Variable on chain schema was set228 ///229 /// # Arguments230 ///231 /// * collection_id: Globally unique collection identifier.232 VariableOnChainSchemaSet(CollectionId),233 }234}235236type SelfWeightOf<T> = <T as Config>::WeightInfo;237238// # Used definitions239//240// ## User control levels241//242// chain-controlled - key is uncontrolled by user243// i.e autoincrementing index244// can use non-cryptographic hash245// real - key is controlled by user246// but it is hard to generate enough colliding values, i.e owner of signed txs247// can use non-cryptographic hash248// controlled - key is completly controlled by users249// i.e maps with mutable keys250// should use cryptographic hash251//252// ## User control level downgrade reasons253//254// ?1 - chain-controlled -> controlled255// collections/tokens can be destroyed, resulting in massive holes256// ?2 - chain-controlled -> controlled257// same as ?1, but can be only added, resulting in easier exploitation258// ?3 - real -> controlled259// no confirmation required, so addresses can be easily generated260decl_storage! {261 trait Store for Module<T: Config> as Unique {262263 //#region Private members264 /// Used for migrations265 ChainVersion: u64;266 //#endregion267268 //#region Tokens transfer rate limit baskets269 /// (Collection id (controlled?2), who created (real))270 /// TODO: Off chain worker should remove from this map when collection gets removed271 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;272 /// Collection id (controlled?2), token id (controlled?2)273 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;274 /// Collection id (controlled?2), owning user (real)275 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;276 /// Collection id (controlled?2), token id (controlled?2)277 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;278 //#endregion279280 /// Variable metadata sponsoring281 /// Collection id (controlled?2), token id (controlled?2)282 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;283 /// Approval sponsoring284 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;285 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;286 pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;287 }288}289290decl_module! {291 pub struct Module<T: Config> for enum Call292 where293 origin: T::Origin294 {295 type Error = Error<T>;296297 fn deposit_event() = default;298299 fn on_initialize(_now: T::BlockNumber) -> Weight {300 0301 }302303 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.304 ///305 /// # Permissions306 ///307 /// * Anyone.308 ///309 /// # Arguments310 ///311 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.312 ///313 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.314 ///315 /// * token_prefix: UTF-8 string with token prefix.316 ///317 /// * mode: [CollectionMode] collection type and type dependent data.318 // returns collection ID319 #[weight = <SelfWeightOf<T>>::create_collection()]320 #[transactional]321 #[deprecated]322 pub fn create_collection(origin,323 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,324 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,325 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,326 mode: CollectionMode) -> DispatchResult {327 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {328 name: collection_name,329 description: collection_description,330 token_prefix,331 mode,332 ..Default::default()333 };334 Self::create_collection_ex(origin, data)335 }336337 /// This method creates a collection338 ///339 /// Prefer it to deprecated [`created_collection`] method340 #[weight = <SelfWeightOf<T>>::create_collection()]341 #[transactional]342 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {343 let sender = ensure_signed(origin)?;344345 // =========346347 T::CollectionDispatch::create(sender, data)?;348349 Ok(())350 }351352 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.353 ///354 /// # Permissions355 ///356 /// * Collection Owner.357 ///358 /// # Arguments359 ///360 /// * collection_id: collection to destroy.361 #[weight = <SelfWeightOf<T>>::destroy_collection()]362 #[transactional]363 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {364 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);365 let collection = <CollectionHandle<T>>::try_get(collection_id)?;366367 // =========368369 T::CollectionDispatch::destroy(sender, collection)?;370371 <NftTransferBasket<T>>::remove_prefix(collection_id, None);372 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);373 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);374375 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);376 <NftApproveBasket<T>>::remove_prefix(collection_id, None);377 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);378 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);379380 Ok(())381 }382383 /// Add an address to allow list.384 ///385 /// # Permissions386 ///387 /// * Collection Owner388 /// * Collection Admin389 ///390 /// # Arguments391 ///392 /// * collection_id.393 ///394 /// * address.395 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]396 #[transactional]397 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{398399 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);400 let collection = <CollectionHandle<T>>::try_get(collection_id)?;401402 <PalletCommon<T>>::toggle_allowlist(403 &collection,404 &sender,405 &address,406 true,407 )?;408409 Self::deposit_event(Event::<T>::AllowListAddressAdded(410 collection_id,411 address412 ));413414 Ok(())415 }416417 /// Remove an address from allow list.418 ///419 /// # Permissions420 ///421 /// * Collection Owner422 /// * Collection Admin423 ///424 /// # Arguments425 ///426 /// * collection_id.427 ///428 /// * address.429 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]430 #[transactional]431 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{432433 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);434 let collection = <CollectionHandle<T>>::try_get(collection_id)?;435436 <PalletCommon<T>>::toggle_allowlist(437 &collection,438 &sender,439 &address,440 false,441 )?;442443 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(444 collection_id,445 address446 ));447448 Ok(())449 }450451 /// Toggle between normal and allow list access for the methods with access for `Anyone`.452 ///453 /// # Permissions454 ///455 /// * Collection Owner.456 ///457 /// # Arguments458 ///459 /// * collection_id.460 ///461 /// * mode: [AccessMode]462 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]463 #[transactional]464 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult465 {466 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);467468 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;469 target_collection.check_is_owner(&sender)?;470471 target_collection.access = mode.clone();472473 <Pallet<T>>::deposit_event(Event::<T>::PublicAccessModeSet(474 collection_id,475 mode476 ));477478 target_collection.save()479 }480481 /// Allows Anyone to create tokens if:482 /// * Allow List is enabled, and483 /// * Address is added to allow list, and484 /// * This method was called with True parameter485 ///486 /// # Permissions487 /// * Collection Owner488 ///489 /// # Arguments490 ///491 /// * collection_id.492 ///493 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.494 #[weight = <SelfWeightOf<T>>::set_mint_permission()]495 #[transactional]496 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult497 {498 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);499500 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;501 target_collection.check_is_owner(&sender)?;502503 target_collection.mint_mode = mint_permission;504505 <Pallet<T>>::deposit_event(Event::<T>::MintPermissionSet(506 collection_id507 ));508509 target_collection.save()510 }511512 /// Change the owner of the collection.513 ///514 /// # Permissions515 ///516 /// * Collection Owner.517 ///518 /// # Arguments519 ///520 /// * collection_id.521 ///522 /// * new_owner.523 #[weight = <SelfWeightOf<T>>::change_collection_owner()]524 #[transactional]525 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {526527 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);528529 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;530 target_collection.check_is_owner(&sender)?;531532 target_collection.owner = new_owner.clone();533 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(534 collection_id,535 new_owner536 ));537538 target_collection.save()539 }540541 /// Adds an admin of the Collection.542 /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.543 ///544 /// # Permissions545 ///546 /// * Collection Owner.547 /// * Collection Admin.548 ///549 /// # Arguments550 ///551 /// * collection_id: ID of the Collection to add admin for.552 ///553 /// * new_admin_id: Address of new admin to add.554 #[weight = <SelfWeightOf<T>>::add_collection_admin()]555 #[transactional]556 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {557 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);558 let collection = <CollectionHandle<T>>::try_get(collection_id)?;559560 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(561 collection_id,562 new_admin_id.clone()563 ));564565 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)566 }567568 /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.569 ///570 /// # Permissions571 ///572 /// * Collection Owner.573 /// * Collection Admin.574 ///575 /// # Arguments576 ///577 /// * collection_id: ID of the Collection to remove admin for.578 ///579 /// * account_id: Address of admin to remove.580 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]581 #[transactional]582 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {583 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);584 let collection = <CollectionHandle<T>>::try_get(collection_id)?;585586 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(587 collection_id,588 account_id.clone()589 ));590591 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)592 }593594 /// # Permissions595 ///596 /// * Collection Owner597 ///598 /// # Arguments599 ///600 /// * collection_id.601 ///602 /// * new_sponsor.603 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]604 #[transactional]605 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {606 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);607608 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;609 target_collection.check_is_owner(&sender)?;610611 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());612613 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(614 collection_id,615 new_sponsor616 ));617618 target_collection.save()619 }620621 /// # Permissions622 ///623 /// * Sponsor.624 ///625 /// # Arguments626 ///627 /// * collection_id.628 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]629 #[transactional]630 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {631 let sender = ensure_signed(origin)?;632633 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;634 ensure!(635 target_collection.sponsorship.pending_sponsor() == Some(&sender),636 Error::<T>::ConfirmUnsetSponsorFail637 );638639 target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());640641 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(642 collection_id,643 sender644 ));645646 target_collection.save()647 }648649 /// Switch back to pay-per-own-transaction model.650 ///651 /// # Permissions652 ///653 /// * Collection owner.654 ///655 /// # Arguments656 ///657 /// * collection_id.658 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]659 #[transactional]660 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {661 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);662663 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;664 target_collection.check_is_owner(&sender)?;665666 target_collection.sponsorship = SponsorshipState::Disabled;667668 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(669 collection_id670 ));671 target_collection.save()672 }673674 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.675 ///676 /// # Permissions677 ///678 /// * Collection Owner.679 /// * Collection Admin.680 /// * Anyone if681 /// * Allow List is enabled, and682 /// * Address is added to allow list, and683 /// * MintPermission is enabled (see SetMintPermission method)684 ///685 /// # Arguments686 ///687 /// * collection_id: ID of the collection.688 ///689 /// * owner: Address, initial owner of the NFT.690 ///691 /// * data: Token data to store on chain.692 #[weight = <CommonWeights<T>>::create_item()]693 #[transactional]694 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {695 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);696697 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data))698 }699700 /// This method creates multiple items in a collection created with CreateCollection method.701 ///702 /// # Permissions703 ///704 /// * Collection Owner.705 /// * Collection Admin.706 /// * Anyone if707 /// * Allow List is enabled, and708 /// * Address is added to allow list, and709 /// * MintPermission is enabled (see SetMintPermission method)710 ///711 /// # Arguments712 ///713 /// * collection_id: ID of the collection.714 ///715 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].716 ///717 /// * owner: Address, initial owner of the NFT.718 #[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]719 #[transactional]720 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {721 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);722 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);723724 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))725 }726727 #[weight = <CommonWeights<T>>::create_multiple_items_ex(&data)]728 #[transactional]729 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {730 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);731732 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data))733 }734735 // TODO! transaction weight736737 /// Set transfers_enabled value for particular collection738 ///739 /// # Permissions740 ///741 /// * Collection Owner.742 ///743 /// # Arguments744 ///745 /// * collection_id: ID of the collection.746 ///747 /// * value: New flag value.748 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]749 #[transactional]750 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {751 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);752 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;753 target_collection.check_is_owner(&sender)?;754755 // =========756757 target_collection.limits.transfers_enabled = Some(value);758 target_collection.save()759 }760761 /// Destroys a concrete instance of NFT.762 ///763 /// # Permissions764 ///765 /// * Collection Owner.766 /// * Collection Admin.767 /// * Current NFT Owner.768 ///769 /// # Arguments770 ///771 /// * collection_id: ID of the collection.772 ///773 /// * item_id: ID of NFT to burn.774 #[weight = <CommonWeights<T>>::burn_item()]775 #[transactional]776 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {777 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);778779 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;780 if value == 1 {781 <NftTransferBasket<T>>::remove(collection_id, item_id);782 <NftApproveBasket<T>>::remove(collection_id, item_id);783 }784 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?785 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());786 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));787 Ok(post_info)788 }789790 /// Destroys a concrete instance of NFT on behalf of the owner791 /// See also: [`approve`]792 ///793 /// # Permissions794 ///795 /// * Collection Owner.796 /// * Collection Admin.797 /// * Current NFT Owner.798 ///799 /// # Arguments800 ///801 /// * collection_id: ID of the collection.802 ///803 /// * item_id: ID of NFT to burn.804 ///805 /// * from: owner of item806 #[weight = <CommonWeights<T>>::burn_from()]807 #[transactional]808 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {809 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);810811 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))812 }813814 /// Change ownership of the token.815 ///816 /// # Permissions817 ///818 /// * Collection Owner819 /// * Collection Admin820 /// * Current NFT owner821 ///822 /// # Arguments823 ///824 /// * recipient: Address of token recipient.825 ///826 /// * collection_id.827 ///828 /// * item_id: ID of the item829 /// * Non-Fungible Mode: Required.830 /// * Fungible Mode: Ignored.831 /// * Re-Fungible Mode: Required.832 ///833 /// * value: Amount to transfer.834 /// * Non-Fungible Mode: Ignored835 /// * Fungible Mode: Must specify transferred amount836 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)837 #[weight = <CommonWeights<T>>::transfer()]838 #[transactional]839 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {840 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);841842 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value))843 }844845 /// Set, change, or remove approved address to transfer the ownership of the NFT.846 ///847 /// # Permissions848 ///849 /// * Collection Owner850 /// * Collection Admin851 /// * Current NFT owner852 ///853 /// # Arguments854 ///855 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).856 ///857 /// * collection_id.858 ///859 /// * item_id: ID of the item.860 #[weight = <CommonWeights<T>>::approve()]861 #[transactional]862 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {863 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);864865 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))866 }867868 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.869 ///870 /// # Permissions871 /// * Collection Owner872 /// * Collection Admin873 /// * Current NFT owner874 /// * Address approved by current NFT owner875 ///876 /// # Arguments877 ///878 /// * from: Address that owns token.879 ///880 /// * recipient: Address of token recipient.881 ///882 /// * collection_id.883 ///884 /// * item_id: ID of the item.885 ///886 /// * value: Amount to transfer.887 #[weight = <CommonWeights<T>>::transfer_from()]888 #[transactional]889 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {890 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);891892 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value))893 }894895 /// Set off-chain data schema.896 ///897 /// # Permissions898 ///899 /// * Collection Owner900 /// * Collection Admin901 ///902 /// # Arguments903 ///904 /// * collection_id.905 ///906 /// * schema: String representing the offchain data schema.907 #[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]908 #[transactional]909 pub fn set_variable_meta_data (910 origin,911 collection_id: CollectionId,912 item_id: TokenId,913 data: BoundedVec<u8, CustomDataLimit>,914 ) -> DispatchResultWithPostInfo {915 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);916917 dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))918 }919920 /// Set meta_update_permission value for particular collection921 ///922 /// # Permissions923 ///924 /// * Collection Owner.925 ///926 /// # Arguments927 ///928 /// * collection_id: ID of the collection.929 ///930 /// * value: New flag value.931 #[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]932 #[transactional]933 pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {934 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);935 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;936937 ensure!(938 target_collection.meta_update_permission != MetaUpdatePermission::None,939 <CommonError<T>>::MetadataFlagFrozen,940 );941 target_collection.check_is_owner(&sender)?;942943 target_collection.meta_update_permission = value;944945 target_collection.save()946 }947948 /// Set schema standard949 /// ImageURL950 /// Unique951 ///952 /// # Permissions953 ///954 /// * Collection Owner955 /// * Collection Admin956 ///957 /// # Arguments958 ///959 /// * collection_id.960 ///961 /// * schema: SchemaVersion: enum962 #[weight = <SelfWeightOf<T>>::set_schema_version()]963 #[transactional]964 pub fn set_schema_version(965 origin,966 collection_id: CollectionId,967 version: SchemaVersion968 ) -> DispatchResult {969 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);970 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;971 target_collection.check_is_owner_or_admin(&sender)?;972 target_collection.schema_version = version;973974 <Pallet<T>>::deposit_event(Event::<T>::SchemaVersionSet(975 collection_id976 ));977978 target_collection.save()979 }980981 /// Set off-chain data schema.982 ///983 /// # Permissions984 ///985 /// * Collection Owner986 /// * Collection Admin987 ///988 /// # Arguments989 ///990 /// * collection_id.991 ///992 /// * schema: String representing the offchain data schema.993 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]994 #[transactional]995 pub fn set_offchain_schema(996 origin,997 collection_id: CollectionId,998 schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,999 ) -> DispatchResult {1000 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1001 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1002 target_collection.check_is_owner_or_admin(&sender)?;10031004 target_collection.offchain_schema = schema;10051006 <Pallet<T>>::deposit_event(Event::<T>::OffchainSchemaSet(1007 collection_id1008 ));10091010 target_collection.save()1011 }10121013 /// Set const on-chain data schema.1014 ///1015 /// # Permissions1016 ///1017 /// * Collection Owner1018 /// * Collection Admin1019 ///1020 /// # Arguments1021 ///1022 /// * collection_id.1023 ///1024 /// * schema: String representing the const on-chain data schema.1025 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1026 #[transactional]1027 pub fn set_const_on_chain_schema (1028 origin,1029 collection_id: CollectionId,1030 schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>1031 ) -> DispatchResult {1032 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1033 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1034 target_collection.check_is_owner_or_admin(&sender)?;10351036 target_collection.const_on_chain_schema = schema;10371038 <Pallet<T>>::deposit_event(Event::<T>::ConstOnChainSchemaSet(1039 collection_id1040 ));10411042 target_collection.save()1043 }10441045 /// Set variable on-chain data schema.1046 ///1047 /// # Permissions1048 ///1049 /// * Collection Owner1050 /// * Collection Admin1051 ///1052 /// # Arguments1053 ///1054 /// * collection_id.1055 ///1056 /// * schema: String representing the variable on-chain data schema.1057 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1058 #[transactional]1059 pub fn set_variable_on_chain_schema (1060 origin,1061 collection_id: CollectionId,1062 schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>1063 ) -> DispatchResult {1064 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1065 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1066 target_collection.check_is_owner_or_admin(&sender)?;10671068 target_collection.variable_on_chain_schema = schema;10691070 <Pallet<T>>::deposit_event(Event::<T>::VariableOnChainSchemaSet(1071 collection_id1072 ));10731074 target_collection.save()1075 }10761077 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1078 #[transactional]1079 pub fn set_collection_limits(1080 origin,1081 collection_id: CollectionId,1082 new_limit: CollectionLimits,1083 ) -> DispatchResult {1084 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1085 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1086 target_collection.check_is_owner(&sender)?;1087 let old_limit = &target_collection.limits;10881089 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;10901091 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1092 collection_id1093 ));10941095 target_collection.save()1096 }1097 }1098}runtime/common/Cargo.tomldiffbeforeafterboth--- a/runtime/common/Cargo.toml
+++ b/runtime/common/Cargo.toml
@@ -12,12 +12,19 @@
default = ['std']
std = [
'sp-core/std',
+ 'sp-std/std',
'sp-runtime/std',
'codec/std',
'frame-support/std',
'frame-system/std',
'sp-consensus-aura/std',
'pallet-common/std',
+ 'pallet-unique/std',
+ 'pallet-fungible/std',
+ 'pallet-nonfungible/std',
+ 'pallet-refungible/std',
+ 'up-data-structs/std',
+ 'pallet-evm/std',
'fp-rpc/std',
]
runtime-benchmarks = [
@@ -31,6 +38,11 @@
git = "https://github.com/paritytech/substrate"
branch = "polkadot-v0.9.20"
+[dependencies.sp-std]
+default-features = false
+git = 'https://github.com/paritytech/substrate.git'
+branch = 'polkadot-v0.9.17'
+
[dependencies.sp-runtime]
default-features = false
git = "https://github.com/paritytech/substrate"
@@ -61,6 +73,31 @@
default-features = false
path = "../../pallets/common"
+[dependencies.pallet-unique]
+default-features = false
+path = "../../pallets/unique"
+
+[dependencies.pallet-fungible]
+default-features = false
+path = "../../pallets/fungible"
+
+[dependencies.pallet-nonfungible]
+default-features = false
+path = "../../pallets/nonfungible"
+
+[dependencies.pallet-refungible]
+default-features = false
+path = "../../pallets/refungible"
+
+[dependencies.up-data-structs]
+default-features = false
+path = "../../primitives/data-structs"
+
+[dependencies.pallet-evm]
+default-features = false
+git = "https://github.com/uniquenetwork/frontier.git"
+branch = "unique-polkadot-v0.9.17"
+
[dependencies.sp-consensus-aura]
default-features = false
git = "https://github.com/paritytech/substrate"
runtime/common/src/dispatch.rsdiffbeforeafterboth--- /dev/null
+++ b/runtime/common/src/dispatch.rs
@@ -0,0 +1,160 @@
+use frame_support::{dispatch::DispatchResult, ensure};
+use pallet_evm::PrecompileResult;
+use sp_core::{H160, U256};
+use sp_std::{borrow::ToOwned, vec::Vec};
+use pallet_common::{
+ CollectionById, CollectionHandle, CommonCollectionOperations, erc::CommonEvmHandler,
+ eth::map_eth_to_id,
+};
+pub use pallet_common::dispatch::CollectionDispatch;
+use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};
+use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};
+use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle, erc::RefungibleTokenHandle};
+use up_data_structs::{
+ CollectionMode, CreateCollectionData, MAX_DECIMAL_POINTS, mapping::TokenAddressMapping,
+};
+
+pub enum CollectionDispatchT<T>
+where
+ T: pallet_fungible::Config + pallet_nonfungible::Config + pallet_refungible::Config,
+{
+ Fungible(FungibleHandle<T>),
+ Nonfungible(NonfungibleHandle<T>),
+ Refungible(RefungibleHandle<T>),
+}
+impl<T> CollectionDispatch<T> for CollectionDispatchT<T>
+where
+ T: pallet_common::Config
+ + pallet_unique::Config
+ + pallet_fungible::Config
+ + pallet_nonfungible::Config
+ + pallet_refungible::Config,
+{
+ fn create(sender: T::AccountId, data: CreateCollectionData<T::AccountId>) -> DispatchResult {
+ let _id = match data.mode {
+ CollectionMode::NFT => <PalletNonfungible<T>>::init_collection(sender, data)?,
+ CollectionMode::Fungible(decimal_points) => {
+ // check params
+ ensure!(
+ decimal_points <= MAX_DECIMAL_POINTS,
+ pallet_unique::Error::<T>::CollectionDecimalPointLimitExceeded
+ );
+ <PalletFungible<T>>::init_collection(sender, data)?
+ }
+ CollectionMode::ReFungible => <PalletRefungible<T>>::init_collection(sender, data)?,
+ };
+ Ok(())
+ }
+
+ fn destroy(sender: T::CrossAccountId, collection: CollectionHandle<T>) -> DispatchResult {
+ match collection.mode {
+ CollectionMode::ReFungible => {
+ PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?
+ }
+ CollectionMode::Fungible(_) => {
+ PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?
+ }
+ CollectionMode::NFT => {
+ PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?
+ }
+ }
+ Ok(())
+ }
+
+ fn dispatch(handle: CollectionHandle<T>) -> Self {
+ match handle.mode {
+ CollectionMode::Fungible(_) => Self::Fungible(FungibleHandle::cast(handle)),
+ CollectionMode::NFT => Self::Nonfungible(NonfungibleHandle::cast(handle)),
+ CollectionMode::ReFungible => Self::Refungible(RefungibleHandle::cast(handle)),
+ }
+ }
+
+ fn into_inner(self) -> CollectionHandle<T> {
+ match self {
+ Self::Fungible(f) => f.into_inner(),
+ Self::Nonfungible(f) => f.into_inner(),
+ Self::Refungible(f) => f.into_inner(),
+ }
+ }
+
+ fn as_dyn(&self) -> &dyn CommonCollectionOperations<T> {
+ match self {
+ Self::Fungible(h) => h,
+ Self::Nonfungible(h) => h,
+ Self::Refungible(h) => h,
+ }
+ }
+}
+
+impl<T> pallet_evm::OnMethodCall<T> for CollectionDispatchT<T>
+where
+ T: pallet_common::Config
+ + pallet_unique::Config
+ + pallet_fungible::Config
+ + pallet_nonfungible::Config
+ + pallet_refungible::Config,
+{
+ fn is_reserved(target: &H160) -> bool {
+ map_eth_to_id(target).is_some()
+ }
+ fn is_used(target: &H160) -> bool {
+ map_eth_to_id(target)
+ .map(<CollectionById<T>>::contains_key)
+ .unwrap_or(false)
+ }
+ fn get_code(target: &H160) -> Option<Vec<u8>> {
+ if let Some(collection_id) = map_eth_to_id(target) {
+ let collection = <CollectionById<T>>::get(collection_id)?;
+ Some(
+ match collection.mode {
+ CollectionMode::NFT => <NonfungibleHandle<T>>::CODE,
+ CollectionMode::Fungible(_) => <FungibleHandle<T>>::CODE,
+ CollectionMode::ReFungible => <RefungibleHandle<T>>::CODE,
+ }
+ .to_owned(),
+ )
+ } else if let Some((collection_id, _token_id)) =
+ <T as pallet_common::Config>::EvmTokenAddressMapping::address_to_token(target)
+ {
+ let collection = <CollectionById<T>>::get(collection_id)?;
+ if collection.mode != CollectionMode::ReFungible {
+ return None;
+ }
+ // TODO: check token existence
+ Some(<RefungibleTokenHandle<T>>::CODE.to_owned())
+ } else {
+ None
+ }
+ }
+ fn call(
+ source: &H160,
+ target: &H160,
+ gas_limit: u64,
+ input: &[u8],
+ value: U256,
+ ) -> Option<PrecompileResult> {
+ if let Some(collection_id) = map_eth_to_id(target) {
+ let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;
+ let dispatched = Self::dispatch(collection);
+
+ match dispatched {
+ Self::Fungible(h) => h.call(source, input, value),
+ Self::Nonfungible(h) => h.call(source, input, value),
+ Self::Refungible(h) => h.call(source, input, value),
+ }
+ } else if let Some((collection_id, token_id)) =
+ <T as pallet_common::Config>::EvmTokenAddressMapping::address_to_token(target)
+ {
+ let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;
+ if collection.mode != CollectionMode::ReFungible {
+ return None;
+ }
+
+ let handle = RefungibleHandle::cast(collection);
+ // TODO: check token existence
+ RefungibleTokenHandle(handle, token_id).call(source, input, value)
+ } else {
+ None
+ }
+ }
+}
runtime/common/src/lib.rsdiffbeforeafterboth--- a/runtime/common/src/lib.rs
+++ b/runtime/common/src/lib.rs
@@ -1,5 +1,6 @@
#![cfg_attr(not(feature = "std"), no_std)]
pub mod constants;
+pub mod dispatch;
pub mod runtime_apis;
pub mod types;
runtime/opal/src/lib.rsdiffbeforeafterboth--- a/runtime/opal/src/lib.rs
+++ b/runtime/opal/src/lib.rs
@@ -58,7 +58,7 @@
traits::{
tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,
Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,
- OnUnbalanced, Randomness, FindAuthor,
+ OnUnbalanced, Randomness, FindAuthor, ConstU32, Imbalance,
},
weights::{
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
@@ -66,7 +66,7 @@
WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, ConstantMultiplier,
},
};
-use up_data_structs::*;
+use up_data_structs::{CollectionId, TokenId, CollectionStats, Collection};
// use pallet_contracts::weights::WeightInfo;
// #[cfg(any(feature = "std", test))]
use frame_system::{
@@ -114,7 +114,12 @@
//use xcm_executor::traits::MatchesFungible;
use sp_runtime::traits::CheckedConversion;
-use unique_runtime_common::{impl_common_runtime_apis, types::*, constants::*};
+use unique_runtime_common::{
+ impl_common_runtime_apis,
+ types::*,
+ constants::*,
+ dispatch::{CollectionDispatchT, CollectionDispatch},
+};
pub const RUNTIME_NAME: &str = "opal";
pub const TOKEN_SYMBOL: &str = "OPL";
@@ -295,8 +300,8 @@
type Event = Event;
type OnMethodCall = (
pallet_evm_migration::OnMethodCall<Self>,
- pallet_unique::UniqueErcSupport<Self>,
pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,
+ CollectionDispatchT<Self>,
);
type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;
type ChainId = ChainId;
@@ -871,8 +876,18 @@
type Currency = Balances;
type CollectionCreationPrice = CollectionCreationPrice;
type TreasuryAccountId = TreasuryAccountId;
+ type CollectionDispatch = CollectionDispatchT<Self>;
+
+ type EvmTokenAddressMapping = EvmTokenAddressMapping;
+ type CrossTokenAddressMapping = CrossTokenAddressMapping<Self::AccountId>;
}
+impl pallet_structure::Config for Runtime {
+ type Event = Event;
+ type Call = Call;
+ type WeightInfo = pallet_structure::weights::SubstrateWeight<Self>;
+}
+
impl pallet_fungible::Config for Runtime {
type WeightInfo = pallet_fungible::weights::SubstrateWeight<Self>;
}
@@ -1119,9 +1134,7 @@
macro_rules! dispatch_unique_runtime {
($collection:ident.$method:ident($($name:ident),*)) => {{
- use pallet_unique::dispatch::Dispatched;
-
- let collection = Dispatched::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);
+ let collection = <Runtime as pallet_common::Config>::CollectionDispatch::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);
let dispatch = collection.as_dyn();
Ok(dispatch.$method($($name),*))
runtime/quartz/src/lib.rsdiffbeforeafterboth--- a/runtime/quartz/src/lib.rs
+++ b/runtime/quartz/src/lib.rs
@@ -58,7 +58,7 @@
traits::{
tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,
Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,
- OnUnbalanced, Randomness, FindAuthor,
+ OnUnbalanced, Randomness, FindAuthor, ConstU32, Imbalance,
},
weights::{
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
@@ -66,6 +66,7 @@
WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, ConstantMultiplier,
},
};
+use unique_runtime_common::dispatch::{CollectionDispatchT, CollectionDispatch};
use up_data_structs::*;
// use pallet_contracts::weights::WeightInfo;
// #[cfg(any(feature = "std", test))]
@@ -92,6 +93,7 @@
// Polkadot imports
use pallet_xcm::XcmPassthrough;
use polkadot_parachain::primitives::Sibling;
+use up_data_structs::mapping::{CrossTokenAddressMapping, EvmTokenAddressMapping};
use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};
use xcm_builder::{
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,
@@ -274,8 +276,8 @@
type Event = Event;
type OnMethodCall = (
pallet_evm_migration::OnMethodCall<Self>,
- pallet_unique::UniqueErcSupport<Self>,
pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,
+ CollectionDispatchT<Self>,
);
type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;
type ChainId = ChainId;
@@ -851,8 +853,18 @@
type Currency = Balances;
type CollectionCreationPrice = CollectionCreationPrice;
type TreasuryAccountId = TreasuryAccountId;
+ type CollectionDispatch = CollectionDispatchT<Self>;
+
+ type EvmTokenAddressMapping = EvmTokenAddressMapping;
+ type CrossTokenAddressMapping = CrossTokenAddressMapping<Self::AccountId>;
}
+impl pallet_structure::Config for Runtime {
+ type Event = Event;
+ type Call = Call;
+ type WeightInfo = pallet_structure::weights::SubstrateWeight<Self>;
+}
+
impl pallet_evm::account::Config for Runtime {
type CrossAccountId = pallet_evm::account::BasicCrossAccountId<Self>;
type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
@@ -979,6 +991,7 @@
Fungible: pallet_fungible::{Pallet, Storage} = 67,
Refungible: pallet_refungible::{Pallet, Storage} = 68,
Nonfungible: pallet_nonfungible::{Pallet, Storage} = 69,
+ Structure: pallet_structure::{Pallet, Call, Storage, Event<T>} = 70,
// Frontier
EVM: pallet_evm::{Pallet, Config, Call, Storage, Event<T>} = 100,
@@ -1105,9 +1118,7 @@
macro_rules! dispatch_unique_runtime {
($collection:ident.$method:ident($($name:ident),*)) => {{
- use pallet_unique::dispatch::Dispatched;
-
- let collection = Dispatched::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);
+ let collection = <Runtime as pallet_common::Config>::CollectionDispatch::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);
let dispatch = collection.as_dyn();
Ok(dispatch.$method($($name),*))
runtime/unique/src/lib.rsdiffbeforeafterboth--- a/runtime/unique/src/lib.rs
+++ b/runtime/unique/src/lib.rs
@@ -58,7 +58,7 @@
traits::{
tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,
Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,
- OnUnbalanced, Randomness, FindAuthor,
+ OnUnbalanced, Randomness, FindAuthor, ConstU32, Imbalance,
},
weights::{
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
@@ -66,6 +66,7 @@
WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, ConstantMultiplier,
},
};
+use unique_runtime_common::dispatch::{CollectionDispatchT, CollectionDispatch};
use up_data_structs::*;
// use pallet_contracts::weights::WeightInfo;
// #[cfg(any(feature = "std", test))]
@@ -91,6 +92,7 @@
// Polkadot imports
use pallet_xcm::XcmPassthrough;
use polkadot_parachain::primitives::Sibling;
+use up_data_structs::mapping::{CrossTokenAddressMapping, EvmTokenAddressMapping};
use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};
use xcm_builder::{
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,
@@ -273,8 +275,8 @@
type Event = Event;
type OnMethodCall = (
pallet_evm_migration::OnMethodCall<Self>,
- pallet_unique::UniqueErcSupport<Self>,
pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,
+ CollectionDispatchT<Self>,
);
type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;
type ChainId = ChainId;
@@ -849,8 +851,18 @@
type Currency = Balances;
type CollectionCreationPrice = CollectionCreationPrice;
type TreasuryAccountId = TreasuryAccountId;
+ type CollectionDispatch = CollectionDispatchT<Self>;
+
+ type EvmTokenAddressMapping = EvmTokenAddressMapping;
+ type CrossTokenAddressMapping = CrossTokenAddressMapping<Self::AccountId>;
}
+impl pallet_structure::Config for Runtime {
+ type Event = Event;
+ type Call = Call;
+ type WeightInfo = pallet_structure::weights::SubstrateWeight<Self>;
+}
+
impl pallet_evm::account::Config for Runtime {
type CrossAccountId = pallet_evm::account::BasicCrossAccountId<Self>;
type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
@@ -977,6 +989,7 @@
Fungible: pallet_fungible::{Pallet, Storage} = 67,
Refungible: pallet_refungible::{Pallet, Storage} = 68,
Nonfungible: pallet_nonfungible::{Pallet, Storage} = 69,
+ Structure: pallet_structure::{Pallet, Call, Storage, Event<T>} = 70,
// Frontier
EVM: pallet_evm::{Pallet, Config, Call, Storage, Event<T>} = 100,
@@ -1103,9 +1116,7 @@
macro_rules! dispatch_unique_runtime {
($collection:ident.$method:ident($($name:ident),*)) => {{
- use pallet_unique::dispatch::Dispatched;
-
- let collection = Dispatched::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);
+ let collection = <Runtime as pallet_common::Config>::CollectionDispatch::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);
let dispatch = collection.as_dyn();
Ok(dispatch.$method($($name),*))