12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#![warn(missing_docs)]53#![cfg_attr(not(feature = "std"), no_std)]54extern crate alloc;5556use core::{57 ops::{Deref, DerefMut},58 slice::from_ref,59};60use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};61use sp_std::vec::Vec;62use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};63use evm_coder::ToLog;64use frame_support::{65 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},66 ensure,67 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},68 dispatch::Pays,69 transactional, fail,70};71use up_data_structs::{72 AccessMode, COLLECTION_NUMBER_LIMIT, Collection, RpcCollection, CollectionFlags,73 RpcCollectionFlags, CollectionId, CreateItemData, MAX_TOKEN_PREFIX_LENGTH,74 COLLECTION_ADMINS_LIMIT, TokenId, TokenChild, CollectionStats, MAX_TOKEN_OWNERSHIP,75 CollectionMode, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,76 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,77 CreateCollectionData, SponsorshipState, CreateItemExData, SponsoringRateLimit, budget::Budget,78 PhantomType, Property, CollectionProperties as CollectionPropertiesT, TokenProperties,79 PropertiesPermissionMap, PropertyKey, PropertyValue, PropertyPermission, PropertiesError,80 TokenOwnerError, PropertyKeyPermission, TokenData, TrySetProperty, PropertyScope,81 CollectionPermissions,82};83use up_pov_estimate_rpc::PovInfo;8485pub use pallet::*;86use sp_core::H160;87use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};8889#[cfg(feature = "runtime-benchmarks")]90pub mod benchmarking;91pub mod dispatch;92pub mod erc;93pub mod eth;94pub mod helpers;95#[allow(missing_docs)]96pub mod weights;9798pub type SelfWeightOf<T> = <T as Config>::WeightInfo;99100101102103104105106#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]107pub struct CollectionHandle<T: Config> {108 109 pub id: CollectionId,110 collection: Collection<T::AccountId>,111 112 pub recorder: SubstrateRecorder<T>,113}114115impl<T: Config> WithRecorder<T> for CollectionHandle<T> {116 fn recorder(&self) -> &SubstrateRecorder<T> {117 &self.recorder118 }119 fn into_recorder(self) -> SubstrateRecorder<T> {120 self.recorder121 }122}123124impl<T: Config> CollectionHandle<T> {125 126 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {127 Self::new_with_recorder(id, SubstrateRecorder::new(gas_limit))128 }129130 131 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {132 <CollectionById<T>>::get(id).map(|collection| Self {133 id,134 collection,135 recorder,136 })137 }138139 140 141 pub fn new(id: CollectionId) -> Option<Self> {142 Self::new_with_gas_limit(id, u64::MAX)143 }144145 146 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {147 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)148 }149150 151 pub fn consume_store_reads(152 &self,153 reads: u64,154 ) -> pallet_evm_coder_substrate::execution::Result<()> {155 self.recorder().consume_store_reads(reads)156 }157158 159 pub fn consume_store_writes(160 &self,161 writes: u64,162 ) -> pallet_evm_coder_substrate::execution::Result<()> {163 self.recorder().consume_store_writes(writes)164 }165166 167 pub fn consume_store_reads_and_writes(168 &self,169 reads: u64,170 writes: u64,171 ) -> pallet_evm_coder_substrate::execution::Result<()> {172 self.recorder()173 .consume_store_reads_and_writes(reads, writes)174 }175176 177 pub fn save(&self) -> DispatchResult {178 <CollectionById<T>>::insert(self.id, &self.collection);179 Ok(())180 }181182 183 184 185 186 187 pub fn set_sponsor(188 &mut self,189 sender: &T::CrossAccountId,190 sponsor: T::AccountId,191 ) -> DispatchResult {192 self.check_is_internal()?;193 self.check_is_owner_or_admin(sender)?;194195 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());196197 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));198 <PalletEvm<T>>::deposit_log(199 erc::CollectionHelpersEvents::CollectionChanged {200 collection_id: eth::collection_id_to_address(self.id),201 }202 .to_log(T::ContractAddress::get()),203 );204205 self.save()206 }207208 209 210 211 212 213 214 215 216 217 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {218 self.check_is_internal()?;219220 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());221222 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));223 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));224 <PalletEvm<T>>::deposit_log(225 erc::CollectionHelpersEvents::CollectionChanged {226 collection_id: eth::collection_id_to_address(self.id),227 }228 .to_log(T::ContractAddress::get()),229 );230231 self.save()232 }233234 235 236 237 238 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {239 self.check_is_internal()?;240 ensure!(241 self.collection.sponsorship.pending_sponsor() == Some(sender),242 Error::<T>::ConfirmSponsorshipFail243 );244245 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());246247 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));248 <PalletEvm<T>>::deposit_log(249 erc::CollectionHelpersEvents::CollectionChanged {250 collection_id: eth::collection_id_to_address(self.id),251 }252 .to_log(T::ContractAddress::get()),253 );254255 self.save()256 }257258 259 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {260 self.check_is_internal()?;261 self.check_is_owner_or_admin(sender)?;262263 self.collection.sponsorship = SponsorshipState::Disabled;264265 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));266 <PalletEvm<T>>::deposit_log(267 erc::CollectionHelpersEvents::CollectionChanged {268 collection_id: eth::collection_id_to_address(self.id),269 }270 .to_log(T::ContractAddress::get()),271 );272 self.save()273 }274275 276 277 278 279 pub fn force_remove_sponsor(&mut self) -> DispatchResult {280 self.check_is_internal()?;281282 self.collection.sponsorship = SponsorshipState::Disabled;283284 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));285 <PalletEvm<T>>::deposit_log(286 erc::CollectionHelpersEvents::CollectionChanged {287 collection_id: eth::collection_id_to_address(self.id),288 }289 .to_log(T::ContractAddress::get()),290 );291 self.save()292 }293294 295 296 pub fn check_is_internal(&self) -> DispatchResult {297 if self.flags.external {298 return Err(<Error<T>>::CollectionIsExternal)?;299 }300301 Ok(())302 }303304 305 306 pub fn check_is_external(&self) -> DispatchResult {307 if !self.flags.external {308 return Err(<Error<T>>::CollectionIsInternal)?;309 }310311 Ok(())312 }313}314315impl<T: Config> Deref for CollectionHandle<T> {316 type Target = Collection<T::AccountId>;317318 fn deref(&self) -> &Self::Target {319 &self.collection320 }321}322323impl<T: Config> DerefMut for CollectionHandle<T> {324 fn deref_mut(&mut self) -> &mut Self::Target {325 &mut self.collection326 }327}328329impl<T: Config> CollectionHandle<T> {330 331 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {332 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);333 Ok(())334 }335336 337 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {338 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))339 }340341 342 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {343 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);344 Ok(())345 }346347 348 349 350 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {351 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)352 }353354 355 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {356 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)357 }358359 360 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {361 ensure!(362 <Allowlist<T>>::get((self.id, user)),363 <Error<T>>::AddressNotInAllowlist364 );365 Ok(())366 }367368 369 370 371 pub fn change_owner(372 &mut self,373 caller: T::CrossAccountId,374 new_owner: T::CrossAccountId,375 ) -> DispatchResult {376 self.check_is_internal()?;377 self.check_is_owner(&caller)?;378 self.collection.owner = new_owner.as_sub().clone();379380 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(381 self.id,382 new_owner.as_sub().clone(),383 ));384 <PalletEvm<T>>::deposit_log(385 erc::CollectionHelpersEvents::CollectionChanged {386 collection_id: eth::collection_id_to_address(self.id),387 }388 .to_log(T::ContractAddress::get()),389 );390391 self.save()392 }393}394395#[frame_support::pallet]396pub mod pallet {397398 use super::*;399 use dispatch::CollectionDispatch;400 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};401 use frame_support::traits::Currency;402 use up_data_structs::{TokenId, mapping::TokenAddressMapping};403 use scale_info::TypeInfo;404 use weights::WeightInfo;405406 #[pallet::config]407 pub trait Config:408 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo409 {410 411 type WeightInfo: WeightInfo;412413 414 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;415416 417 type Currency: Currency<Self::AccountId>;418419 420 #[pallet::constant]421 type CollectionCreationPrice: Get<422 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,423 >;424425 426 type CollectionDispatch: CollectionDispatch<Self>;427428 429 type TreasuryAccountId: Get<Self::AccountId>;430431 432 #[pallet::constant]433 type ContractAddress: Get<H160>;434435 436 type EvmTokenAddressMapping: TokenAddressMapping<H160>;437438 439 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;440 }441442 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);443 444 pub const NATIVE_FUNGIBLE_COLLECTION_ID: CollectionId = CollectionId(0);445446 #[pallet::pallet]447 #[pallet::storage_version(STORAGE_VERSION)]448 pub struct Pallet<T>(_);449450 #[pallet::extra_constants]451 impl<T: Config> Pallet<T> {452 453 pub fn collection_admins_limit() -> u32 {454 COLLECTION_ADMINS_LIMIT455 }456 }457458 #[pallet::genesis_config]459 pub struct GenesisConfig<T>(PhantomData<T>);460461 #[cfg(feature = "std")]462 impl<T: Config> Default for GenesisConfig<T> {463 fn default() -> Self {464 Self(Default::default())465 }466 }467468 #[pallet::genesis_build]469 impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {470 fn build(&self) {471 StorageVersion::new(1).put::<Pallet<T>>();472 }473 }474475 impl<T: Config> Pallet<T> {476 477 pub fn deposit_event(event: Event<T>) {478 let event = <T as Config>::RuntimeEvent::from(event);479 let event = event.into();480 <frame_system::Pallet<T>>::deposit_event(event)481 }482 }483484 #[pallet::event]485 pub enum Event<T: Config> {486 487 CollectionCreated(488 489 CollectionId,490 491 u8,492 493 T::AccountId,494 ),495496 497 CollectionDestroyed(498 499 CollectionId,500 ),501502 503 ItemCreated(504 505 CollectionId,506 507 TokenId,508 509 T::CrossAccountId,510 511 u128,512 ),513514 515 ItemDestroyed(516 517 CollectionId,518 519 TokenId,520 521 T::CrossAccountId,522 523 u128,524 ),525526 527 Transfer(528 529 CollectionId,530 531 TokenId,532 533 T::CrossAccountId,534 535 T::CrossAccountId,536 537 u128,538 ),539540 541 Approved(542 543 CollectionId,544 545 TokenId,546 547 T::CrossAccountId,548 549 T::CrossAccountId,550 551 u128,552 ),553554 555 ApprovedForAll(556 557 CollectionId,558 559 T::CrossAccountId,560 561 T::CrossAccountId,562 563 bool,564 ),565566 567 CollectionPropertySet(568 569 CollectionId,570 571 PropertyKey,572 ),573574 575 CollectionPropertyDeleted(576 577 CollectionId,578 579 PropertyKey,580 ),581582 583 TokenPropertySet(584 585 CollectionId,586 587 TokenId,588 589 PropertyKey,590 ),591592 593 TokenPropertyDeleted(594 595 CollectionId,596 597 TokenId,598 599 PropertyKey,600 ),601602 603 PropertyPermissionSet(604 605 CollectionId,606 607 PropertyKey,608 ),609610 611 AllowListAddressAdded(612 613 CollectionId,614 615 T::CrossAccountId,616 ),617618 619 AllowListAddressRemoved(620 621 CollectionId,622 623 T::CrossAccountId,624 ),625626 627 CollectionAdminAdded(628 629 CollectionId,630 631 T::CrossAccountId,632 ),633634 635 CollectionAdminRemoved(636 637 CollectionId,638 639 T::CrossAccountId,640 ),641642 643 CollectionLimitSet(644 645 CollectionId,646 ),647648 649 CollectionOwnerChanged(650 651 CollectionId,652 653 T::AccountId,654 ),655656 657 CollectionPermissionSet(658 659 CollectionId,660 ),661662 663 CollectionSponsorSet(664 665 CollectionId,666 667 T::AccountId,668 ),669670 671 SponsorshipConfirmed(672 673 CollectionId,674 675 T::AccountId,676 ),677678 679 CollectionSponsorRemoved(680 681 CollectionId,682 ),683 }684685 #[pallet::error]686 pub enum Error<T> {687 688 CollectionNotFound,689 690 MustBeTokenOwner,691 692 NoPermission,693 694 CantDestroyNotEmptyCollection,695 696 PublicMintingNotAllowed,697 698 AddressNotInAllowlist,699700 701 CollectionNameLimitExceeded,702 703 CollectionDescriptionLimitExceeded,704 705 CollectionTokenPrefixLimitExceeded,706 707 TotalCollectionsLimitExceeded,708 709 CollectionAdminCountExceeded,710 711 CollectionLimitBoundsExceeded,712 713 OwnerPermissionsCantBeReverted,714 715 TransferNotAllowed,716 717 AccountTokenLimitExceeded,718 719 CollectionTokenLimitExceeded,720 721 MetadataFlagFrozen,722723 724 TokenNotFound,725 726 TokenValueTooLow,727 728 ApprovedValueTooLow,729 730 CantApproveMoreThanOwned,731 732 AddressIsNotEthMirror,733734 735 AddressIsZero,736737 738 UnsupportedOperation,739740 741 NotSufficientFounds,742743 744 UserIsNotAllowedToNest,745 746 SourceCollectionIsNotAllowedToNest,747748 749 CollectionFieldSizeExceeded,750751 752 NoSpaceForProperty,753754 755 PropertyLimitReached,756757 758 PropertyKeyIsTooLong,759760 761 InvalidCharacterInPropertyKey,762763 764 EmptyPropertyKey,765766 767 CollectionIsExternal,768769 770 CollectionIsInternal,771772 773 ConfirmSponsorshipFail,774775 776 UserIsNotCollectionAdmin,777 }778779 780 #[pallet::storage]781 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;782783 784 #[pallet::storage]785 pub type DestroyedCollectionCount<T> =786 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;787788 789 #[pallet::storage]790 pub type CollectionById<T> = StorageMap<791 Hasher = Blake2_128Concat,792 Key = CollectionId,793 Value = Collection<<T as frame_system::Config>::AccountId>,794 QueryKind = OptionQuery,795 >;796797 798 #[pallet::storage]799 #[pallet::getter(fn collection_properties)]800 pub type CollectionProperties<T> = StorageMap<801 Hasher = Blake2_128Concat,802 Key = CollectionId,803 Value = CollectionPropertiesT,804 QueryKind = ValueQuery,805 >;806807 808 #[pallet::storage]809 #[pallet::getter(fn property_permissions)]810 pub type CollectionPropertyPermissions<T> = StorageMap<811 Hasher = Blake2_128Concat,812 Key = CollectionId,813 Value = PropertiesPermissionMap,814 QueryKind = ValueQuery,815 >;816817 818 #[pallet::storage]819 pub type AdminAmount<T> = StorageMap<820 Hasher = Blake2_128Concat,821 Key = CollectionId,822 Value = u32,823 QueryKind = ValueQuery,824 >;825826 827 #[pallet::storage]828 pub type IsAdmin<T: Config> = StorageNMap<829 Key = (830 Key<Blake2_128Concat, CollectionId>,831 Key<Blake2_128Concat, T::CrossAccountId>,832 ),833 Value = bool,834 QueryKind = ValueQuery,835 >;836837 838 #[pallet::storage]839 pub type Allowlist<T: Config> = StorageNMap<840 Key = (841 Key<Blake2_128Concat, CollectionId>,842 Key<Blake2_128Concat, T::CrossAccountId>,843 ),844 Value = bool,845 QueryKind = ValueQuery,846 >;847848 849 #[pallet::storage]850 pub type DummyStorageValue<T: Config> = StorageValue<851 Value = (852 CollectionStats,853 CollectionId,854 TokenId,855 TokenChild,856 PhantomType<(857 TokenData<T::CrossAccountId>,858 RpcCollection<T::AccountId>,859 860 PovInfo,861 )>,862 ),863 QueryKind = OptionQuery,864 >;865}866867impl<T: Config> Pallet<T> {868 869 870 871 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {872 ensure!(873 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,874 <Error<T>>::AddressIsZero875 );876 Ok(())877 }878879 880 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {881 <IsAdmin<T>>::iter_prefix((collection,))882 .map(|(a, _)| a)883 .collect()884 }885886 887 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {888 <Allowlist<T>>::iter_prefix((collection,))889 .map(|(a, _)| a)890 .collect()891 }892893 894 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {895 <Allowlist<T>>::get((collection, user))896 }897898 899 pub fn collection_stats() -> CollectionStats {900 let created = <CreatedCollectionCount<T>>::get();901 let destroyed = <DestroyedCollectionCount<T>>::get();902 CollectionStats {903 created: created.0,904 destroyed: destroyed.0,905 alive: created.0 - destroyed.0,906 }907 }908909 910 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {911 let collection = <CollectionById<T>>::get(collection)?;912 let limits = collection.limits;913 let effective_limits = CollectionLimits {914 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),915 sponsored_data_size: Some(limits.sponsored_data_size()),916 sponsored_data_rate_limit: Some(917 limits918 .sponsored_data_rate_limit919 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),920 ),921 token_limit: Some(limits.token_limit()),922 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(923 match collection.mode {924 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,925 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,926 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,927 },928 )),929 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),930 owner_can_transfer: Some(limits.owner_can_transfer()),931 owner_can_destroy: Some(limits.owner_can_destroy()),932 transfers_enabled: Some(limits.transfers_enabled()),933 };934935 Some(effective_limits)936 }937938 939 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {940 let Collection {941 name,942 description,943 owner,944 mode,945 token_prefix,946 sponsorship,947 limits,948 permissions,949 flags,950 } = <CollectionById<T>>::get(collection)?;951952 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)953 .into_iter()954 .map(|(key, permission)| PropertyKeyPermission { key, permission })955 .collect();956957 let properties = <CollectionProperties<T>>::get(collection)958 .into_iter()959 .map(|(key, value)| Property { key, value })960 .collect();961962 let permissions = CollectionPermissions {963 access: Some(permissions.access()),964 mint_mode: Some(permissions.mint_mode()),965 nesting: Some(permissions.nesting().clone()),966 };967968 Some(RpcCollection {969 name: name.into_inner(),970 description: description.into_inner(),971 owner,972 mode,973 token_prefix: token_prefix.into_inner(),974 sponsorship,975 limits,976 permissions,977 token_property_permissions,978 properties,979 read_only: flags.external,980981 flags: RpcCollectionFlags {982 foreign: flags.foreign,983 erc721metadata: flags.erc721metadata,984 },985 })986 }987}988989macro_rules! limit_default {990 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{991 $(992 if let Some($new) = $new.$field {993 let $old = $old.$field($($arg)?);994 let _ = $new;995 let _ = $old;996 $check997 } else {998 $new.$field = $old.$field999 }1000 )*1001 }};1002}1003macro_rules! limit_default_clone {1004 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1005 $(1006 if let Some($new) = $new.$field.clone() {1007 let $old = $old.$field($($arg)?);1008 let _ = $new;1009 let _ = $old;1010 $check1011 } else {1012 $new.$field = $old.$field.clone()1013 }1014 )*1015 }};1016}10171018impl<T: Config> Pallet<T> {1019 1020 1021 1022 1023 1024 pub fn init_collection(1025 owner: T::CrossAccountId,1026 payer: T::CrossAccountId,1027 data: CreateCollectionData<T::AccountId>,1028 flags: CollectionFlags,1029 ) -> Result<CollectionId, DispatchError> {1030 {1031 ensure!(1032 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1033 Error::<T>::CollectionTokenPrefixLimitExceeded1034 );1035 }10361037 let created_count = <CreatedCollectionCount<T>>::get()1038 .01039 .checked_add(1)1040 .ok_or(ArithmeticError::Overflow)?;1041 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1042 let id = CollectionId(created_count);10431044 1045 ensure!(1046 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1047 <Error<T>>::TotalCollectionsLimitExceeded1048 );10491050 10511052 let collection = Collection {1053 owner: owner.as_sub().clone(),1054 name: data.name,1055 mode: data.mode.clone(),1056 description: data.description,1057 token_prefix: data.token_prefix,1058 sponsorship: data1059 .pending_sponsor1060 .map(SponsorshipState::Unconfirmed)1061 .unwrap_or_default(),1062 limits: data1063 .limits1064 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1065 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1066 permissions: data1067 .permissions1068 .map(|permissions| {1069 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1070 })1071 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1072 flags,1073 };10741075 let mut collection_properties = CollectionPropertiesT::new();1076 collection_properties1077 .try_set_from_iter(data.properties.into_iter())1078 .map_err(<Error<T>>::from)?;10791080 CollectionProperties::<T>::insert(id, collection_properties);10811082 let mut token_props_permissions = PropertiesPermissionMap::new();1083 token_props_permissions1084 .try_set_from_iter(data.token_property_permissions.into_iter())1085 .map_err(<Error<T>>::from)?;10861087 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);10881089 1090 {1091 let mut imbalance =1092 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1093 imbalance.subsume(1094 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1095 &T::TreasuryAccountId::get(),1096 T::CollectionCreationPrice::get(),1097 ),1098 );1099 <T as Config>::Currency::settle(1100 payer.as_sub(),1101 imbalance,1102 WithdrawReasons::TRANSFER,1103 ExistenceRequirement::KeepAlive,1104 )1105 .map_err(|_| Error::<T>::NotSufficientFounds)?;1106 }11071108 <CreatedCollectionCount<T>>::put(created_count);1109 <Pallet<T>>::deposit_event(Event::CollectionCreated(1110 id,1111 data.mode.id(),1112 owner.as_sub().clone(),1113 ));1114 <PalletEvm<T>>::deposit_log(1115 erc::CollectionHelpersEvents::CollectionCreated {1116 owner: *owner.as_eth(),1117 collection_id: eth::collection_id_to_address(id),1118 }1119 .to_log(T::ContractAddress::get()),1120 );1121 <CollectionById<T>>::insert(id, collection);1122 Ok(id)1123 }11241125 1126 1127 1128 1129 pub fn destroy_collection(1130 collection: CollectionHandle<T>,1131 sender: &T::CrossAccountId,1132 ) -> DispatchResult {1133 ensure!(1134 collection.limits.owner_can_destroy(),1135 <Error<T>>::NoPermission,1136 );1137 collection.check_is_owner(sender)?;11381139 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1140 .01141 .checked_add(1)1142 .ok_or(ArithmeticError::Overflow)?;11431144 11451146 <DestroyedCollectionCount<T>>::put(destroyed_collections);1147 <CollectionById<T>>::remove(collection.id);1148 <AdminAmount<T>>::remove(collection.id);1149 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1150 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1151 <CollectionProperties<T>>::remove(collection.id);11521153 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11541155 <PalletEvm<T>>::deposit_log(1156 erc::CollectionHelpersEvents::CollectionDestroyed {1157 collection_id: eth::collection_id_to_address(collection.id),1158 }1159 .to_log(T::ContractAddress::get()),1160 );1161 Ok(())1162 }11631164 1165 1166 1167 1168 1169 1170 1171 1172 #[transactional]1173 fn modify_collection_properties(1174 collection: &CollectionHandle<T>,1175 sender: &T::CrossAccountId,1176 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1177 ) -> DispatchResult {1178 collection.check_is_owner_or_admin(sender)?;11791180 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);11811182 for (key, value) in properties_updates {1183 match value {1184 Some(value) => {1185 stored_properties1186 .try_set(key.clone(), value)1187 .map_err(<Error<T>>::from)?;11881189 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1190 <PalletEvm<T>>::deposit_log(1191 erc::CollectionHelpersEvents::CollectionChanged {1192 collection_id: eth::collection_id_to_address(collection.id),1193 }1194 .to_log(T::ContractAddress::get()),1195 );1196 }1197 None => {1198 stored_properties.remove(&key).map_err(<Error<T>>::from)?;11991200 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1201 <PalletEvm<T>>::deposit_log(1202 erc::CollectionHelpersEvents::CollectionChanged {1203 collection_id: eth::collection_id_to_address(collection.id),1204 }1205 .to_log(T::ContractAddress::get()),1206 );1207 }1208 }1209 }12101211 <CollectionProperties<T>>::set(collection.id, stored_properties);12121213 Ok(())1214 }12151216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 pub fn modify_token_properties(1234 collection: &CollectionHandle<T>,1235 sender: &T::CrossAccountId,1236 token_id: TokenId,1237 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1238 is_token_create: bool,1239 mut stored_properties: TokenProperties,1240 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1241 set_token_properties: impl FnOnce(TokenProperties),1242 log: evm_coder::ethereum::Log,1243 ) -> DispatchResult {1244 let is_collection_admin = collection.is_owner_or_admin(sender);1245 let permissions = Self::property_permissions(collection.id);12461247 let mut token_owner_result = None;1248 let mut is_token_owner = || -> Result<bool, DispatchError> {1249 *token_owner_result.get_or_insert_with(&is_token_owner)1250 };12511252 for (key, value) in properties_updates {1253 let permission = permissions1254 .get(&key)1255 .cloned()1256 .unwrap_or_else(PropertyPermission::none);12571258 let is_property_exists = stored_properties.get(&key).is_some();12591260 match permission {1261 PropertyPermission { mutable: false, .. } if is_property_exists => {1262 return Err(<Error<T>>::NoPermission.into());1263 }12641265 PropertyPermission {1266 collection_admin,1267 token_owner,1268 ..1269 } => {1270 1271 let is_token_create =1272 is_token_create && (collection_admin || token_owner) && value.is_some();1273 if !(is_token_create1274 || (collection_admin && is_collection_admin)1275 || (token_owner && is_token_owner()?))1276 {1277 fail!(<Error<T>>::NoPermission);1278 }1279 }1280 }12811282 match value {1283 Some(value) => {1284 stored_properties1285 .try_set(key.clone(), value)1286 .map_err(<Error<T>>::from)?;12871288 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1289 }1290 None => {1291 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12921293 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1294 }1295 }12961297 <PalletEvm<T>>::deposit_log(log.clone());1298 }12991300 set_token_properties(stored_properties);13011302 Ok(())1303 }13041305 1306 1307 1308 1309 1310 1311 pub fn set_allowance_for_all(1312 collection: &CollectionHandle<T>,1313 owner: &T::CrossAccountId,1314 operator: &T::CrossAccountId,1315 approve: bool,1316 set_allowance: impl FnOnce(),1317 log: evm_coder::ethereum::Log,1318 ) -> DispatchResult {1319 if collection.permissions.access() == AccessMode::AllowList {1320 collection.check_allowlist(owner)?;1321 collection.check_allowlist(operator)?;1322 }13231324 Self::ensure_correct_receiver(operator)?;13251326 set_allowance();13271328 <PalletEvm<T>>::deposit_log(log);1329 Self::deposit_event(Event::ApprovedForAll(1330 collection.id,1331 owner.clone(),1332 operator.clone(),1333 approve,1334 ));1335 Ok(())1336 }13371338 1339 1340 1341 1342 1343 pub fn set_collection_property(1344 collection: &CollectionHandle<T>,1345 sender: &T::CrossAccountId,1346 property: Property,1347 ) -> DispatchResult {1348 Self::set_collection_properties(collection, sender, [property].into_iter())1349 }13501351 1352 1353 1354 1355 1356 1357 pub fn set_scoped_collection_property(1358 collection_id: CollectionId,1359 scope: PropertyScope,1360 property: Property,1361 ) -> DispatchResult {1362 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1363 properties.try_scoped_set(scope, property.key, property.value)1364 })1365 .map_err(<Error<T>>::from)?;13661367 Ok(())1368 }13691370 1371 1372 1373 1374 1375 1376 pub fn set_scoped_collection_properties(1377 collection_id: CollectionId,1378 scope: PropertyScope,1379 properties: impl Iterator<Item = Property>,1380 ) -> DispatchResult {1381 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1382 stored_properties.try_scoped_set_from_iter(scope, properties)1383 })1384 .map_err(<Error<T>>::from)?;13851386 Ok(())1387 }13881389 1390 1391 1392 1393 1394 pub fn set_collection_properties(1395 collection: &CollectionHandle<T>,1396 sender: &T::CrossAccountId,1397 properties: impl Iterator<Item = Property>,1398 ) -> DispatchResult {1399 Self::modify_collection_properties(1400 collection,1401 sender,1402 properties.map(|property| (property.key, Some(property.value))),1403 )1404 }14051406 1407 1408 1409 1410 1411 pub fn delete_collection_property(1412 collection: &CollectionHandle<T>,1413 sender: &T::CrossAccountId,1414 property_key: PropertyKey,1415 ) -> DispatchResult {1416 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1417 }14181419 1420 1421 1422 1423 1424 pub fn delete_collection_properties(1425 collection: &CollectionHandle<T>,1426 sender: &T::CrossAccountId,1427 property_keys: impl Iterator<Item = PropertyKey>,1428 ) -> DispatchResult {1429 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1430 }14311432 1433 1434 1435 1436 1437 1438 pub fn set_property_permission_unchecked(1439 collection: CollectionId,1440 property_permission: PropertyKeyPermission,1441 ) -> DispatchResult {1442 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1443 permissions.try_set(property_permission.key, property_permission.permission)1444 })1445 .map_err(<Error<T>>::from)?;1446 Ok(())1447 }14481449 1450 1451 1452 1453 1454 pub fn set_property_permission(1455 collection: &CollectionHandle<T>,1456 sender: &T::CrossAccountId,1457 property_permission: PropertyKeyPermission,1458 ) -> DispatchResult {1459 Self::set_scoped_property_permission(1460 collection,1461 sender,1462 PropertyScope::None,1463 property_permission,1464 )1465 }14661467 1468 1469 1470 1471 1472 1473 pub fn set_scoped_property_permission(1474 collection: &CollectionHandle<T>,1475 sender: &T::CrossAccountId,1476 scope: PropertyScope,1477 property_permission: PropertyKeyPermission,1478 ) -> DispatchResult {1479 collection.check_is_owner_or_admin(sender)?;14801481 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1482 let current_permission = all_permissions.get(&property_permission.key);1483 if matches![1484 current_permission,1485 Some(PropertyPermission { mutable: false, .. })1486 ] {1487 return Err(<Error<T>>::NoPermission.into());1488 }14891490 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1491 let property_permission = property_permission.clone();1492 permissions.try_scoped_set(1493 scope,1494 property_permission.key,1495 property_permission.permission,1496 )1497 })1498 .map_err(<Error<T>>::from)?;14991500 Self::deposit_event(Event::PropertyPermissionSet(1501 collection.id,1502 property_permission.key,1503 ));1504 <PalletEvm<T>>::deposit_log(1505 erc::CollectionHelpersEvents::CollectionChanged {1506 collection_id: eth::collection_id_to_address(collection.id),1507 }1508 .to_log(T::ContractAddress::get()),1509 );15101511 Ok(())1512 }15131514 1515 1516 1517 1518 1519 #[transactional]1520 pub fn set_token_property_permissions(1521 collection: &CollectionHandle<T>,1522 sender: &T::CrossAccountId,1523 property_permissions: Vec<PropertyKeyPermission>,1524 ) -> DispatchResult {1525 Self::set_scoped_token_property_permissions(1526 collection,1527 sender,1528 PropertyScope::None,1529 property_permissions,1530 )1531 }15321533 1534 1535 1536 1537 1538 1539 #[transactional]1540 pub fn set_scoped_token_property_permissions(1541 collection: &CollectionHandle<T>,1542 sender: &T::CrossAccountId,1543 scope: PropertyScope,1544 property_permissions: Vec<PropertyKeyPermission>,1545 ) -> DispatchResult {1546 for prop_pemission in property_permissions {1547 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1548 }15491550 Ok(())1551 }15521553 1554 pub fn get_collection_property(1555 collection_id: CollectionId,1556 key: &PropertyKey,1557 ) -> Option<PropertyValue> {1558 Self::collection_properties(collection_id).get(key).cloned()1559 }15601561 1562 pub fn bytes_keys_to_property_keys(1563 keys: Vec<Vec<u8>>,1564 ) -> Result<Vec<PropertyKey>, DispatchError> {1565 keys.into_iter()1566 .map(|key| -> Result<PropertyKey, DispatchError> {1567 key.try_into()1568 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1569 })1570 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1571 }15721573 1574 pub fn filter_collection_properties(1575 collection_id: CollectionId,1576 keys: Option<Vec<PropertyKey>>,1577 ) -> Result<Vec<Property>, DispatchError> {1578 let properties = Self::collection_properties(collection_id);15791580 let properties = keys1581 .map(|keys| {1582 keys.into_iter()1583 .filter_map(|key| {1584 properties.get(&key).map(|value| Property {1585 key,1586 value: value.clone(),1587 })1588 })1589 .collect()1590 })1591 .unwrap_or_else(|| {1592 properties1593 .into_iter()1594 .map(|(key, value)| Property { key, value })1595 .collect()1596 });15971598 Ok(properties)1599 }16001601 1602 pub fn filter_property_permissions(1603 collection_id: CollectionId,1604 keys: Option<Vec<PropertyKey>>,1605 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1606 let permissions = Self::property_permissions(collection_id);16071608 let key_permissions = keys1609 .map(|keys| {1610 keys.into_iter()1611 .filter_map(|key| {1612 permissions1613 .get(&key)1614 .map(|permission| PropertyKeyPermission {1615 key,1616 permission: permission.clone(),1617 })1618 })1619 .collect()1620 })1621 .unwrap_or_else(|| {1622 permissions1623 .into_iter()1624 .map(|(key, permission)| PropertyKeyPermission { key, permission })1625 .collect()1626 });16271628 Ok(key_permissions)1629 }16301631 1632 1633 1634 pub fn toggle_allowlist(1635 collection: &CollectionHandle<T>,1636 sender: &T::CrossAccountId,1637 user: &T::CrossAccountId,1638 allowed: bool,1639 ) -> DispatchResult {1640 collection.check_is_owner_or_admin(sender)?;16411642 16431644 if allowed {1645 <Allowlist<T>>::insert((collection.id, user), true);1646 Self::deposit_event(Event::<T>::AllowListAddressAdded(1647 collection.id,1648 user.clone(),1649 ));1650 } else {1651 <Allowlist<T>>::remove((collection.id, user));1652 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1653 collection.id,1654 user.clone(),1655 ));1656 }16571658 <PalletEvm<T>>::deposit_log(1659 erc::CollectionHelpersEvents::CollectionChanged {1660 collection_id: eth::collection_id_to_address(collection.id),1661 }1662 .to_log(T::ContractAddress::get()),1663 );16641665 Ok(())1666 }16671668 1669 1670 1671 pub fn toggle_admin(1672 collection: &CollectionHandle<T>,1673 sender: &T::CrossAccountId,1674 user: &T::CrossAccountId,1675 admin: bool,1676 ) -> DispatchResult {1677 collection.check_is_internal()?;1678 collection.check_is_owner(sender)?;16791680 let is_admin = <IsAdmin<T>>::get((collection.id, user));1681 if is_admin == admin {1682 if admin {1683 return Ok(());1684 } else {1685 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1686 }1687 }1688 let amount = <AdminAmount<T>>::get(collection.id);16891690 16911692 if admin {1693 let amount = amount1694 .checked_add(1)1695 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1696 ensure!(1697 amount <= Self::collection_admins_limit(),1698 <Error<T>>::CollectionAdminCountExceeded,1699 );17001701 <AdminAmount<T>>::insert(collection.id, amount);1702 <IsAdmin<T>>::insert((collection.id, user), true);17031704 Self::deposit_event(Event::<T>::CollectionAdminAdded(1705 collection.id,1706 user.clone(),1707 ));1708 } else {1709 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1710 <IsAdmin<T>>::remove((collection.id, user));17111712 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1713 collection.id,1714 user.clone(),1715 ));1716 }17171718 <PalletEvm<T>>::deposit_log(1719 erc::CollectionHelpersEvents::CollectionChanged {1720 collection_id: eth::collection_id_to_address(collection.id),1721 }1722 .to_log(T::ContractAddress::get()),1723 );17241725 Ok(())1726 }17271728 1729 pub fn update_limits(1730 user: &T::CrossAccountId,1731 collection: &mut CollectionHandle<T>,1732 new_limit: CollectionLimits,1733 ) -> DispatchResult {1734 collection.check_is_internal()?;1735 collection.check_is_owner_or_admin(user)?;17361737 collection.limits =1738 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;17391740 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1741 <PalletEvm<T>>::deposit_log(1742 erc::CollectionHelpersEvents::CollectionChanged {1743 collection_id: eth::collection_id_to_address(collection.id),1744 }1745 .to_log(T::ContractAddress::get()),1746 );17471748 collection.save()1749 }17501751 1752 fn clamp_limits(1753 mode: CollectionMode,1754 old_limit: &CollectionLimits,1755 mut new_limit: CollectionLimits,1756 ) -> Result<CollectionLimits, DispatchError> {1757 let limits = old_limit;1758 limit_default!(old_limit, new_limit,1759 account_token_ownership_limit => ensure!(1760 new_limit <= MAX_TOKEN_OWNERSHIP,1761 <Error<T>>::CollectionLimitBoundsExceeded,1762 ),1763 sponsored_data_size => ensure!(1764 new_limit <= CUSTOM_DATA_LIMIT,1765 <Error<T>>::CollectionLimitBoundsExceeded,1766 ),17671768 sponsored_data_rate_limit => {},1769 token_limit => ensure!(1770 old_limit >= new_limit && new_limit > 0,1771 <Error<T>>::CollectionTokenLimitExceeded1772 ),17731774 sponsor_transfer_timeout(match mode {1775 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1776 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1777 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1778 }) => ensure!(1779 new_limit <= MAX_SPONSOR_TIMEOUT,1780 <Error<T>>::CollectionLimitBoundsExceeded,1781 ),1782 sponsor_approve_timeout => {},1783 owner_can_transfer => ensure!(1784 !limits.owner_can_transfer_instaled() ||1785 old_limit || !new_limit,1786 <Error<T>>::OwnerPermissionsCantBeReverted,1787 ),1788 owner_can_destroy => ensure!(1789 old_limit || !new_limit,1790 <Error<T>>::OwnerPermissionsCantBeReverted,1791 ),1792 transfers_enabled => {},1793 );1794 Ok(new_limit)1795 }17961797 1798 pub fn update_permissions(1799 user: &T::CrossAccountId,1800 collection: &mut CollectionHandle<T>,1801 new_permission: CollectionPermissions,1802 ) -> DispatchResult {1803 collection.check_is_internal()?;1804 collection.check_is_owner_or_admin(user)?;1805 collection.permissions = Self::clamp_permissions(1806 collection.mode.clone(),1807 &collection.permissions,1808 new_permission,1809 )?;18101811 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1812 <PalletEvm<T>>::deposit_log(1813 erc::CollectionHelpersEvents::CollectionChanged {1814 collection_id: eth::collection_id_to_address(collection.id),1815 }1816 .to_log(T::ContractAddress::get()),1817 );18181819 collection.save()1820 }18211822 1823 fn clamp_permissions(1824 _mode: CollectionMode,1825 old_permission: &CollectionPermissions,1826 mut new_permission: CollectionPermissions,1827 ) -> Result<CollectionPermissions, DispatchError> {1828 limit_default_clone!(old_permission, new_permission,1829 access => {},1830 mint_mode => {},1831 nesting => { },1832 );1833 Ok(new_permission)1834 }18351836 1837 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1838 CollectionProperties::<T>::mutate(collection_id, |properties| {1839 properties.recompute_consumed_space();1840 });18411842 Ok(())1843 }1844}184518461847#[macro_export]1848macro_rules! unsupported {1849 ($runtime:path) => {1850 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1851 };1852}185318541855pub trait CommonWeightInfo<CrossAccountId> {1856 1857 fn create_item(data: &CreateItemData) -> Weight {1858 Self::create_multiple_items(from_ref(data))1859 }18601861 1862 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;18631864 1865 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;18661867 1868 fn burn_item() -> Weight;18691870 1871 1872 1873 fn set_collection_properties(amount: u32) -> Weight;18741875 1876 1877 1878 fn delete_collection_properties(amount: u32) -> Weight;18791880 1881 1882 1883 fn set_token_properties(amount: u32) -> Weight;18841885 1886 1887 1888 fn delete_token_properties(amount: u32) -> Weight;18891890 1891 1892 1893 fn set_token_property_permissions(amount: u32) -> Weight;18941895 1896 fn transfer() -> Weight;18971898 1899 fn approve() -> Weight;19001901 1902 fn approve_from() -> Weight;19031904 1905 fn transfer_from() -> Weight;19061907 1908 fn burn_from() -> Weight;19091910 1911 1912 1913 1914 fn burn_recursively_self_raw() -> Weight;19151916 1917 1918 1919 fn burn_recursively_breadth_raw(amount: u32) -> Weight;19201921 1922 1923 1924 1925 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1926 Self::burn_recursively_self_raw()1927 .saturating_mul(max_selfs.max(1) as u64)1928 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1929 }19301931 1932 fn token_owner() -> Weight;19331934 1935 fn set_allowance_for_all() -> Weight;19361937 1938 fn force_repair_item() -> Weight;1939}194019411942pub trait RefungibleExtensionsWeightInfo {1943 1944 fn repartition() -> Weight;1945}194619471948194919501951pub trait CommonCollectionOperations<T: Config> {1952 1953 1954 1955 1956 1957 1958 fn create_item(1959 &self,1960 sender: T::CrossAccountId,1961 to: T::CrossAccountId,1962 data: CreateItemData,1963 nesting_budget: &dyn Budget,1964 ) -> DispatchResultWithPostInfo;19651966 1967 1968 1969 1970 1971 1972 fn create_multiple_items(1973 &self,1974 sender: T::CrossAccountId,1975 to: T::CrossAccountId,1976 data: Vec<CreateItemData>,1977 nesting_budget: &dyn Budget,1978 ) -> DispatchResultWithPostInfo;19791980 1981 1982 1983 1984 1985 1986 fn create_multiple_items_ex(1987 &self,1988 sender: T::CrossAccountId,1989 data: CreateItemExData<T::CrossAccountId>,1990 nesting_budget: &dyn Budget,1991 ) -> DispatchResultWithPostInfo;19921993 1994 1995 1996 1997 1998 fn burn_item(1999 &self,2000 sender: T::CrossAccountId,2001 token: TokenId,2002 amount: u128,2003 ) -> DispatchResultWithPostInfo;20042005 2006 2007 2008 2009 2010 2011 fn burn_item_recursively(2012 &self,2013 sender: T::CrossAccountId,2014 token: TokenId,2015 self_budget: &dyn Budget,2016 breadth_budget: &dyn Budget,2017 ) -> DispatchResultWithPostInfo;20182019 2020 2021 2022 2023 fn set_collection_properties(2024 &self,2025 sender: T::CrossAccountId,2026 properties: Vec<Property>,2027 ) -> DispatchResultWithPostInfo;20282029 2030 2031 2032 2033 fn delete_collection_properties(2034 &self,2035 sender: &T::CrossAccountId,2036 property_keys: Vec<PropertyKey>,2037 ) -> DispatchResultWithPostInfo;20382039 2040 2041 2042 2043 2044 2045 2046 2047 2048 fn set_token_properties(2049 &self,2050 sender: T::CrossAccountId,2051 token_id: TokenId,2052 properties: Vec<Property>,2053 budget: &dyn Budget,2054 ) -> DispatchResultWithPostInfo;20552056 2057 2058 2059 2060 2061 2062 2063 2064 2065 fn delete_token_properties(2066 &self,2067 sender: T::CrossAccountId,2068 token_id: TokenId,2069 property_keys: Vec<PropertyKey>,2070 budget: &dyn Budget,2071 ) -> DispatchResultWithPostInfo;20722073 2074 2075 2076 2077 2078 2079 fn set_token_property_permissions(2080 &self,2081 sender: &T::CrossAccountId,2082 property_permissions: Vec<PropertyKeyPermission>,2083 ) -> DispatchResultWithPostInfo;20842085 2086 2087 2088 2089 2090 2091 2092 fn transfer(2093 &self,2094 sender: T::CrossAccountId,2095 to: T::CrossAccountId,2096 token: TokenId,2097 amount: u128,2098 budget: &dyn Budget,2099 ) -> DispatchResultWithPostInfo;21002101 2102 2103 2104 2105 2106 2107 fn approve(2108 &self,2109 sender: T::CrossAccountId,2110 spender: T::CrossAccountId,2111 token: TokenId,2112 amount: u128,2113 ) -> DispatchResultWithPostInfo;21142115 2116 2117 2118 2119 2120 2121 2122 fn approve_from(2123 &self,2124 sender: T::CrossAccountId,2125 from: T::CrossAccountId,2126 to: T::CrossAccountId,2127 token: TokenId,2128 amount: u128,2129 ) -> DispatchResultWithPostInfo;21302131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 fn transfer_from(2142 &self,2143 sender: T::CrossAccountId,2144 from: T::CrossAccountId,2145 to: T::CrossAccountId,2146 token: TokenId,2147 amount: u128,2148 budget: &dyn Budget,2149 ) -> DispatchResultWithPostInfo;21502151 2152 2153 2154 2155 2156 2157 2158 2159 2160 fn burn_from(2161 &self,2162 sender: T::CrossAccountId,2163 from: T::CrossAccountId,2164 token: TokenId,2165 amount: u128,2166 budget: &dyn Budget,2167 ) -> DispatchResultWithPostInfo;21682169 2170 2171 2172 2173 2174 2175 fn check_nesting(2176 &self,2177 sender: T::CrossAccountId,2178 from: (CollectionId, TokenId),2179 under: TokenId,2180 budget: &dyn Budget,2181 ) -> DispatchResult;21822183 2184 2185 2186 2187 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21882189 2190 2191 2192 2193 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21942195 2196 2197 2198 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21992200 2201 fn collection_tokens(&self) -> Vec<TokenId>;22022203 2204 2205 2206 fn token_exists(&self, token: TokenId) -> bool;22072208 2209 fn last_token_id(&self) -> TokenId;22102211 2212 2213 2214 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22152216 2217 2218 2219 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22202221 2222 2223 2224 2225 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22262227 2228 2229 2230 2231 2232 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22332234 2235 fn total_supply(&self) -> u32;22362237 2238 2239 2240 fn account_balance(&self, account: T::CrossAccountId) -> u32;22412242 2243 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;22442245 2246 fn total_pieces(&self, token: TokenId) -> Option<u128>;22472248 2249 2250 2251 2252 2253 fn allowance(2254 &self,2255 sender: T::CrossAccountId,2256 spender: T::CrossAccountId,2257 token: TokenId,2258 ) -> u128;22592260 2261 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;22622263 2264 2265 2266 2267 fn set_allowance_for_all(2268 &self,2269 owner: T::CrossAccountId,2270 operator: T::CrossAccountId,2271 approve: bool,2272 ) -> DispatchResultWithPostInfo;22732274 2275 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;22762277 2278 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2279}228022812282pub trait RefungibleExtensions<T>2283where2284 T: Config,2285{2286 2287 2288 2289 2290 2291 2292 2293 fn repartition(2294 &self,2295 sender: &T::CrossAccountId,2296 token: TokenId,2297 amount: u128,2298 ) -> DispatchResultWithPostInfo;2299}23002301230223032304pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2305 let post_info = PostDispatchInfo {2306 actual_weight: Some(weight),2307 pays_fee: Pays::Yes,2308 };2309 match res {2310 Ok(()) => Ok(post_info),2311 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2312 }2313}23142315impl<T: Config> From<PropertiesError> for Error<T> {2316 fn from(error: PropertiesError) -> Self {2317 match error {2318 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2319 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2320 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2321 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2322 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2323 }2324 }2325}