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::{68 Get,69 fungible::{Balanced, Debt, Inspect},70 tokens::{Imbalance, Precision, Preservation},71 },72 dispatch::Pays,73 transactional, fail,74};75use up_data_structs::{76 AccessMode, COLLECTION_NUMBER_LIMIT, Collection, RpcCollection, CollectionFlags,77 RpcCollectionFlags, CollectionId, CreateItemData, MAX_TOKEN_PREFIX_LENGTH,78 COLLECTION_ADMINS_LIMIT, TokenId, TokenChild, CollectionStats, MAX_TOKEN_OWNERSHIP,79 CollectionMode, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,80 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,81 CreateCollectionData, SponsorshipState, CreateItemExData, SponsoringRateLimit, budget::Budget,82 PhantomType, Property, CollectionProperties as CollectionPropertiesT, TokenProperties,83 PropertiesPermissionMap, PropertyKey, PropertyValue, PropertyPermission, PropertiesError,84 TokenOwnerError, PropertyKeyPermission, TokenData, TrySetProperty, PropertyScope,85 CollectionPermissions,86};87use up_pov_estimate_rpc::PovInfo;8889pub use pallet::*;90use sp_core::H160;91use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, traits::Zero};9293#[cfg(feature = "runtime-benchmarks")]94pub mod benchmarking;95pub mod dispatch;96pub mod erc;97pub mod eth;98pub mod helpers;99#[allow(missing_docs)]100pub mod weights;101102pub type SelfWeightOf<T> = <T as Config>::WeightInfo;103104105106107108109110#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]111pub struct CollectionHandle<T: Config> {112 113 pub id: CollectionId,114 collection: Collection<T::AccountId>,115 116 pub recorder: SubstrateRecorder<T>,117}118119impl<T: Config> WithRecorder<T> for CollectionHandle<T> {120 fn recorder(&self) -> &SubstrateRecorder<T> {121 &self.recorder122 }123 fn into_recorder(self) -> SubstrateRecorder<T> {124 self.recorder125 }126}127128impl<T: Config> CollectionHandle<T> {129 130 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {131 Self::new_with_recorder(id, SubstrateRecorder::new(gas_limit))132 }133134 135 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {136 <CollectionById<T>>::get(id).map(|collection| Self {137 id,138 collection,139 recorder,140 })141 }142143 144 145 pub fn new(id: CollectionId) -> Option<Self> {146 Self::new_with_gas_limit(id, u64::MAX)147 }148149 150 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {151 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)152 }153154 155 pub fn consume_store_reads(156 &self,157 reads: u64,158 ) -> pallet_evm_coder_substrate::execution::Result<()> {159 self.recorder().consume_store_reads(reads)160 }161162 163 pub fn consume_store_writes(164 &self,165 writes: u64,166 ) -> pallet_evm_coder_substrate::execution::Result<()> {167 self.recorder().consume_store_writes(writes)168 }169170 171 pub fn consume_store_reads_and_writes(172 &self,173 reads: u64,174 writes: u64,175 ) -> pallet_evm_coder_substrate::execution::Result<()> {176 self.recorder()177 .consume_store_reads_and_writes(reads, writes)178 }179180 181 pub fn save(&self) -> DispatchResult {182 <CollectionById<T>>::insert(self.id, &self.collection);183 Ok(())184 }185186 187 188 189 190 191 pub fn set_sponsor(192 &mut self,193 sender: &T::CrossAccountId,194 sponsor: T::AccountId,195 ) -> DispatchResult {196 self.check_is_internal()?;197 self.check_is_owner_or_admin(sender)?;198199 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());200201 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));202 <PalletEvm<T>>::deposit_log(203 erc::CollectionHelpersEvents::CollectionChanged {204 collection_id: eth::collection_id_to_address(self.id),205 }206 .to_log(T::ContractAddress::get()),207 );208209 self.save()210 }211212 213 214 215 216 217 218 219 220 221 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {222 self.check_is_internal()?;223224 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());225226 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));227 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));228 <PalletEvm<T>>::deposit_log(229 erc::CollectionHelpersEvents::CollectionChanged {230 collection_id: eth::collection_id_to_address(self.id),231 }232 .to_log(T::ContractAddress::get()),233 );234235 self.save()236 }237238 239 240 241 242 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {243 self.check_is_internal()?;244 ensure!(245 self.collection.sponsorship.pending_sponsor() == Some(sender),246 Error::<T>::ConfirmSponsorshipFail247 );248249 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());250251 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));252 <PalletEvm<T>>::deposit_log(253 erc::CollectionHelpersEvents::CollectionChanged {254 collection_id: eth::collection_id_to_address(self.id),255 }256 .to_log(T::ContractAddress::get()),257 );258259 self.save()260 }261262 263 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {264 self.check_is_internal()?;265 self.check_is_owner_or_admin(sender)?;266267 self.collection.sponsorship = SponsorshipState::Disabled;268269 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));270 <PalletEvm<T>>::deposit_log(271 erc::CollectionHelpersEvents::CollectionChanged {272 collection_id: eth::collection_id_to_address(self.id),273 }274 .to_log(T::ContractAddress::get()),275 );276 self.save()277 }278279 280 281 282 283 pub fn force_remove_sponsor(&mut self) -> DispatchResult {284 self.check_is_internal()?;285286 self.collection.sponsorship = SponsorshipState::Disabled;287288 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));289 <PalletEvm<T>>::deposit_log(290 erc::CollectionHelpersEvents::CollectionChanged {291 collection_id: eth::collection_id_to_address(self.id),292 }293 .to_log(T::ContractAddress::get()),294 );295 self.save()296 }297298 299 300 pub fn check_is_internal(&self) -> DispatchResult {301 if self.flags.external {302 return Err(<Error<T>>::CollectionIsExternal)?;303 }304305 Ok(())306 }307308 309 310 pub fn check_is_external(&self) -> DispatchResult {311 if !self.flags.external {312 return Err(<Error<T>>::CollectionIsInternal)?;313 }314315 Ok(())316 }317}318319impl<T: Config> Deref for CollectionHandle<T> {320 type Target = Collection<T::AccountId>;321322 fn deref(&self) -> &Self::Target {323 &self.collection324 }325}326327impl<T: Config> DerefMut for CollectionHandle<T> {328 fn deref_mut(&mut self) -> &mut Self::Target {329 &mut self.collection330 }331}332333impl<T: Config> CollectionHandle<T> {334 335 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {336 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);337 Ok(())338 }339340 341 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {342 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))343 }344345 346 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {347 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);348 Ok(())349 }350351 352 353 354 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {355 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)356 }357358 359 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {360 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)361 }362363 364 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {365 ensure!(366 <Allowlist<T>>::get((self.id, user)),367 <Error<T>>::AddressNotInAllowlist368 );369 Ok(())370 }371372 373 374 375 pub fn change_owner(376 &mut self,377 caller: T::CrossAccountId,378 new_owner: T::CrossAccountId,379 ) -> DispatchResult {380 self.check_is_internal()?;381 self.check_is_owner(&caller)?;382 self.collection.owner = new_owner.as_sub().clone();383384 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(385 self.id,386 new_owner.as_sub().clone(),387 ));388 <PalletEvm<T>>::deposit_log(389 erc::CollectionHelpersEvents::CollectionChanged {390 collection_id: eth::collection_id_to_address(self.id),391 }392 .to_log(T::ContractAddress::get()),393 );394395 self.save()396 }397}398399#[frame_support::pallet]400pub mod pallet {401402 use super::*;403 use dispatch::CollectionDispatch;404 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};405 use up_data_structs::{TokenId, mapping::TokenAddressMapping};406 use scale_info::TypeInfo;407 use weights::WeightInfo;408409 #[pallet::config]410 pub trait Config:411 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo412 {413 414 type WeightInfo: WeightInfo;415416 417 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;418419 420 type Currency: Balanced<Self::AccountId> + Inspect<Self::AccountId>;421422 423 #[pallet::constant]424 type CollectionCreationPrice: Get<425 <<Self as Config>::Currency as Inspect<Self::AccountId>>::Balance,426 >;427428 429 type CollectionDispatch: CollectionDispatch<Self>;430431 432 type TreasuryAccountId: Get<Self::AccountId>;433434 435 #[pallet::constant]436 type ContractAddress: Get<H160>;437438 439 type EvmTokenAddressMapping: TokenAddressMapping<H160>;440441 442 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;443 }444445 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);446 447 pub const NATIVE_FUNGIBLE_COLLECTION_ID: CollectionId = CollectionId(0);448449 #[pallet::pallet]450 #[pallet::storage_version(STORAGE_VERSION)]451 pub struct Pallet<T>(_);452453 #[pallet::extra_constants]454 impl<T: Config> Pallet<T> {455 456 pub fn collection_admins_limit() -> u32 {457 COLLECTION_ADMINS_LIMIT458 }459 }460461 #[pallet::genesis_config]462 pub struct GenesisConfig<T>(PhantomData<T>);463464 #[cfg(feature = "std")]465 impl<T: Config> Default for GenesisConfig<T> {466 fn default() -> Self {467 Self(Default::default())468 }469 }470471 #[pallet::genesis_build]472 impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {473 fn build(&self) {474 StorageVersion::new(1).put::<Pallet<T>>();475 }476 }477478 impl<T: Config> Pallet<T> {479 480 pub fn deposit_event(event: Event<T>) {481 let event = <T as Config>::RuntimeEvent::from(event);482 let event = event.into();483 <frame_system::Pallet<T>>::deposit_event(event)484 }485 }486487 #[pallet::event]488 pub enum Event<T: Config> {489 490 CollectionCreated(491 492 CollectionId,493 494 u8,495 496 T::AccountId,497 ),498499 500 CollectionDestroyed(501 502 CollectionId,503 ),504505 506 ItemCreated(507 508 CollectionId,509 510 TokenId,511 512 T::CrossAccountId,513 514 u128,515 ),516517 518 ItemDestroyed(519 520 CollectionId,521 522 TokenId,523 524 T::CrossAccountId,525 526 u128,527 ),528529 530 Transfer(531 532 CollectionId,533 534 TokenId,535 536 T::CrossAccountId,537 538 T::CrossAccountId,539 540 u128,541 ),542543 544 Approved(545 546 CollectionId,547 548 TokenId,549 550 T::CrossAccountId,551 552 T::CrossAccountId,553 554 u128,555 ),556557 558 ApprovedForAll(559 560 CollectionId,561 562 T::CrossAccountId,563 564 T::CrossAccountId,565 566 bool,567 ),568569 570 CollectionPropertySet(571 572 CollectionId,573 574 PropertyKey,575 ),576577 578 CollectionPropertyDeleted(579 580 CollectionId,581 582 PropertyKey,583 ),584585 586 TokenPropertySet(587 588 CollectionId,589 590 TokenId,591 592 PropertyKey,593 ),594595 596 TokenPropertyDeleted(597 598 CollectionId,599 600 TokenId,601 602 PropertyKey,603 ),604605 606 PropertyPermissionSet(607 608 CollectionId,609 610 PropertyKey,611 ),612613 614 AllowListAddressAdded(615 616 CollectionId,617 618 T::CrossAccountId,619 ),620621 622 AllowListAddressRemoved(623 624 CollectionId,625 626 T::CrossAccountId,627 ),628629 630 CollectionAdminAdded(631 632 CollectionId,633 634 T::CrossAccountId,635 ),636637 638 CollectionAdminRemoved(639 640 CollectionId,641 642 T::CrossAccountId,643 ),644645 646 CollectionLimitSet(647 648 CollectionId,649 ),650651 652 CollectionOwnerChanged(653 654 CollectionId,655 656 T::AccountId,657 ),658659 660 CollectionPermissionSet(661 662 CollectionId,663 ),664665 666 CollectionSponsorSet(667 668 CollectionId,669 670 T::AccountId,671 ),672673 674 SponsorshipConfirmed(675 676 CollectionId,677 678 T::AccountId,679 ),680681 682 CollectionSponsorRemoved(683 684 CollectionId,685 ),686 }687688 #[pallet::error]689 pub enum Error<T> {690 691 CollectionNotFound,692 693 MustBeTokenOwner,694 695 NoPermission,696 697 CantDestroyNotEmptyCollection,698 699 PublicMintingNotAllowed,700 701 AddressNotInAllowlist,702703 704 CollectionNameLimitExceeded,705 706 CollectionDescriptionLimitExceeded,707 708 CollectionTokenPrefixLimitExceeded,709 710 TotalCollectionsLimitExceeded,711 712 CollectionAdminCountExceeded,713 714 CollectionLimitBoundsExceeded,715 716 OwnerPermissionsCantBeReverted,717 718 TransferNotAllowed,719 720 AccountTokenLimitExceeded,721 722 CollectionTokenLimitExceeded,723 724 MetadataFlagFrozen,725726 727 TokenNotFound,728 729 TokenValueTooLow,730 731 ApprovedValueTooLow,732 733 CantApproveMoreThanOwned,734 735 AddressIsNotEthMirror,736737 738 AddressIsZero,739740 741 UnsupportedOperation,742743 744 NotSufficientFounds,745746 747 UserIsNotAllowedToNest,748 749 SourceCollectionIsNotAllowedToNest,750751 752 CollectionFieldSizeExceeded,753754 755 NoSpaceForProperty,756757 758 PropertyLimitReached,759760 761 PropertyKeyIsTooLong,762763 764 InvalidCharacterInPropertyKey,765766 767 EmptyPropertyKey,768769 770 CollectionIsExternal,771772 773 CollectionIsInternal,774775 776 ConfirmSponsorshipFail,777778 779 UserIsNotCollectionAdmin,780 }781782 783 #[pallet::storage]784 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;785786 787 #[pallet::storage]788 pub type DestroyedCollectionCount<T> =789 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;790791 792 #[pallet::storage]793 pub type CollectionById<T> = StorageMap<794 Hasher = Blake2_128Concat,795 Key = CollectionId,796 Value = Collection<<T as frame_system::Config>::AccountId>,797 QueryKind = OptionQuery,798 >;799800 801 #[pallet::storage]802 #[pallet::getter(fn collection_properties)]803 pub type CollectionProperties<T> = StorageMap<804 Hasher = Blake2_128Concat,805 Key = CollectionId,806 Value = CollectionPropertiesT,807 QueryKind = ValueQuery,808 >;809810 811 #[pallet::storage]812 #[pallet::getter(fn property_permissions)]813 pub type CollectionPropertyPermissions<T> = StorageMap<814 Hasher = Blake2_128Concat,815 Key = CollectionId,816 Value = PropertiesPermissionMap,817 QueryKind = ValueQuery,818 >;819820 821 #[pallet::storage]822 pub type AdminAmount<T> = StorageMap<823 Hasher = Blake2_128Concat,824 Key = CollectionId,825 Value = u32,826 QueryKind = ValueQuery,827 >;828829 830 #[pallet::storage]831 pub type IsAdmin<T: Config> = StorageNMap<832 Key = (833 Key<Blake2_128Concat, CollectionId>,834 Key<Blake2_128Concat, T::CrossAccountId>,835 ),836 Value = bool,837 QueryKind = ValueQuery,838 >;839840 841 #[pallet::storage]842 pub type Allowlist<T: Config> = StorageNMap<843 Key = (844 Key<Blake2_128Concat, CollectionId>,845 Key<Blake2_128Concat, T::CrossAccountId>,846 ),847 Value = bool,848 QueryKind = ValueQuery,849 >;850851 852 #[pallet::storage]853 pub type DummyStorageValue<T: Config> = StorageValue<854 Value = (855 CollectionStats,856 CollectionId,857 TokenId,858 TokenChild,859 PhantomType<(860 TokenData<T::CrossAccountId>,861 RpcCollection<T::AccountId>,862 863 PovInfo,864 )>,865 ),866 QueryKind = OptionQuery,867 >;868}869870impl<T: Config> Pallet<T> {871 872 873 874 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {875 ensure!(876 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,877 <Error<T>>::AddressIsZero878 );879 Ok(())880 }881882 883 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {884 <IsAdmin<T>>::iter_prefix((collection,))885 .map(|(a, _)| a)886 .collect()887 }888889 890 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {891 <Allowlist<T>>::iter_prefix((collection,))892 .map(|(a, _)| a)893 .collect()894 }895896 897 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {898 <Allowlist<T>>::get((collection, user))899 }900901 902 pub fn collection_stats() -> CollectionStats {903 let created = <CreatedCollectionCount<T>>::get();904 let destroyed = <DestroyedCollectionCount<T>>::get();905 CollectionStats {906 created: created.0,907 destroyed: destroyed.0,908 alive: created.0 - destroyed.0,909 }910 }911912 913 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {914 let collection = <CollectionById<T>>::get(collection)?;915 let limits = collection.limits;916 let effective_limits = CollectionLimits {917 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),918 sponsored_data_size: Some(limits.sponsored_data_size()),919 sponsored_data_rate_limit: Some(920 limits921 .sponsored_data_rate_limit922 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),923 ),924 token_limit: Some(limits.token_limit()),925 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(926 match collection.mode {927 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,928 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,929 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,930 },931 )),932 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),933 owner_can_transfer: Some(limits.owner_can_transfer()),934 owner_can_destroy: Some(limits.owner_can_destroy()),935 transfers_enabled: Some(limits.transfers_enabled()),936 };937938 Some(effective_limits)939 }940941 942 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {943 let Collection {944 name,945 description,946 owner,947 mode,948 token_prefix,949 sponsorship,950 limits,951 permissions,952 flags,953 } = <CollectionById<T>>::get(collection)?;954955 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)956 .into_iter()957 .map(|(key, permission)| PropertyKeyPermission { key, permission })958 .collect();959960 let properties = <CollectionProperties<T>>::get(collection)961 .into_iter()962 .map(|(key, value)| Property { key, value })963 .collect();964965 let permissions = CollectionPermissions {966 access: Some(permissions.access()),967 mint_mode: Some(permissions.mint_mode()),968 nesting: Some(permissions.nesting().clone()),969 };970971 Some(RpcCollection {972 name: name.into_inner(),973 description: description.into_inner(),974 owner,975 mode,976 token_prefix: token_prefix.into_inner(),977 sponsorship,978 limits,979 permissions,980 token_property_permissions,981 properties,982 read_only: flags.external,983984 flags: RpcCollectionFlags {985 foreign: flags.foreign,986 erc721metadata: flags.erc721metadata,987 },988 })989 }990}991992macro_rules! limit_default {993 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{994 $(995 if let Some($new) = $new.$field {996 let $old = $old.$field($($arg)?);997 let _ = $new;998 let _ = $old;999 $check1000 } else {1001 $new.$field = $old.$field1002 }1003 )*1004 }};1005}1006macro_rules! limit_default_clone {1007 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1008 $(1009 if let Some($new) = $new.$field.clone() {1010 let $old = $old.$field($($arg)?);1011 let _ = $new;1012 let _ = $old;1013 $check1014 } else {1015 $new.$field = $old.$field.clone()1016 }1017 )*1018 }};1019}10201021impl<T: Config> Pallet<T> {1022 1023 1024 1025 1026 1027 pub fn init_collection(1028 owner: T::CrossAccountId,1029 payer: T::CrossAccountId,1030 data: CreateCollectionData<T::AccountId>,1031 flags: CollectionFlags,1032 ) -> Result<CollectionId, DispatchError> {1033 {1034 ensure!(1035 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1036 Error::<T>::CollectionTokenPrefixLimitExceeded1037 );1038 }10391040 let created_count = <CreatedCollectionCount<T>>::get()1041 .01042 .checked_add(1)1043 .ok_or(ArithmeticError::Overflow)?;1044 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1045 let id = CollectionId(created_count);10461047 1048 ensure!(1049 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1050 <Error<T>>::TotalCollectionsLimitExceeded1051 );10521053 10541055 let collection = Collection {1056 owner: owner.as_sub().clone(),1057 name: data.name,1058 mode: data.mode.clone(),1059 description: data.description,1060 token_prefix: data.token_prefix,1061 sponsorship: data1062 .pending_sponsor1063 .map(SponsorshipState::Unconfirmed)1064 .unwrap_or_default(),1065 limits: data1066 .limits1067 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1068 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1069 permissions: data1070 .permissions1071 .map(|permissions| {1072 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1073 })1074 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1075 flags,1076 };10771078 let mut collection_properties = CollectionPropertiesT::new();1079 collection_properties1080 .try_set_from_iter(data.properties.into_iter())1081 .map_err(<Error<T>>::from)?;10821083 CollectionProperties::<T>::insert(id, collection_properties);10841085 let mut token_props_permissions = PropertiesPermissionMap::new();1086 token_props_permissions1087 .try_set_from_iter(data.token_property_permissions.into_iter())1088 .map_err(<Error<T>>::from)?;10891090 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);10911092 1093 {1094 let mut imbalance = <Debt<T::AccountId, <T as Config>::Currency>>::zero();1095 imbalance.subsume(<T as Config>::Currency::deposit(1096 &T::TreasuryAccountId::get(),1097 T::CollectionCreationPrice::get(),1098 Precision::Exact,1099 )?);1100 let credit =1101 <T as Config>::Currency::settle(payer.as_sub(), imbalance, Preservation::Preserve)1102 .map_err(|_| Error::<T>::NotSufficientFounds)?;11031104 debug_assert!(credit.peek().is_zero())1105 }11061107 <CreatedCollectionCount<T>>::put(created_count);1108 <Pallet<T>>::deposit_event(Event::CollectionCreated(1109 id,1110 data.mode.id(),1111 owner.as_sub().clone(),1112 ));1113 <PalletEvm<T>>::deposit_log(1114 erc::CollectionHelpersEvents::CollectionCreated {1115 owner: *owner.as_eth(),1116 collection_id: eth::collection_id_to_address(id),1117 }1118 .to_log(T::ContractAddress::get()),1119 );1120 <CollectionById<T>>::insert(id, collection);1121 Ok(id)1122 }11231124 1125 1126 1127 1128 pub fn destroy_collection(1129 collection: CollectionHandle<T>,1130 sender: &T::CrossAccountId,1131 ) -> DispatchResult {1132 ensure!(1133 collection.limits.owner_can_destroy(),1134 <Error<T>>::NoPermission,1135 );1136 collection.check_is_owner(sender)?;11371138 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1139 .01140 .checked_add(1)1141 .ok_or(ArithmeticError::Overflow)?;11421143 11441145 <DestroyedCollectionCount<T>>::put(destroyed_collections);1146 <CollectionById<T>>::remove(collection.id);1147 <AdminAmount<T>>::remove(collection.id);1148 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1149 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1150 <CollectionProperties<T>>::remove(collection.id);11511152 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11531154 <PalletEvm<T>>::deposit_log(1155 erc::CollectionHelpersEvents::CollectionDestroyed {1156 collection_id: eth::collection_id_to_address(collection.id),1157 }1158 .to_log(T::ContractAddress::get()),1159 );1160 Ok(())1161 }11621163 1164 1165 1166 1167 1168 1169 1170 1171 #[transactional]1172 fn modify_collection_properties(1173 collection: &CollectionHandle<T>,1174 sender: &T::CrossAccountId,1175 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1176 ) -> DispatchResult {1177 collection.check_is_owner_or_admin(sender)?;11781179 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);11801181 for (key, value) in properties_updates {1182 match value {1183 Some(value) => {1184 stored_properties1185 .try_set(key.clone(), value)1186 .map_err(<Error<T>>::from)?;11871188 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1189 <PalletEvm<T>>::deposit_log(1190 erc::CollectionHelpersEvents::CollectionChanged {1191 collection_id: eth::collection_id_to_address(collection.id),1192 }1193 .to_log(T::ContractAddress::get()),1194 );1195 }1196 None => {1197 stored_properties.remove(&key).map_err(<Error<T>>::from)?;11981199 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1200 <PalletEvm<T>>::deposit_log(1201 erc::CollectionHelpersEvents::CollectionChanged {1202 collection_id: eth::collection_id_to_address(collection.id),1203 }1204 .to_log(T::ContractAddress::get()),1205 );1206 }1207 }1208 }12091210 <CollectionProperties<T>>::set(collection.id, stored_properties);12111212 Ok(())1213 }12141215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 pub fn modify_token_properties(1233 collection: &CollectionHandle<T>,1234 sender: &T::CrossAccountId,1235 token_id: TokenId,1236 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1237 is_token_create: bool,1238 mut stored_properties: TokenProperties,1239 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1240 set_token_properties: impl FnOnce(TokenProperties),1241 log: evm_coder::ethereum::Log,1242 ) -> DispatchResult {1243 let is_collection_admin = collection.is_owner_or_admin(sender);1244 let permissions = Self::property_permissions(collection.id);12451246 let mut token_owner_result = None;1247 let mut is_token_owner = || -> Result<bool, DispatchError> {1248 *token_owner_result.get_or_insert_with(&is_token_owner)1249 };12501251 for (key, value) in properties_updates {1252 let permission = permissions1253 .get(&key)1254 .cloned()1255 .unwrap_or_else(PropertyPermission::none);12561257 let is_property_exists = stored_properties.get(&key).is_some();12581259 match permission {1260 PropertyPermission { mutable: false, .. } if is_property_exists => {1261 return Err(<Error<T>>::NoPermission.into());1262 }12631264 PropertyPermission {1265 collection_admin,1266 token_owner,1267 ..1268 } => {1269 1270 let is_token_create =1271 is_token_create && (collection_admin || token_owner) && value.is_some();1272 if !(is_token_create1273 || (collection_admin && is_collection_admin)1274 || (token_owner && is_token_owner()?))1275 {1276 fail!(<Error<T>>::NoPermission);1277 }1278 }1279 }12801281 match value {1282 Some(value) => {1283 stored_properties1284 .try_set(key.clone(), value)1285 .map_err(<Error<T>>::from)?;12861287 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1288 }1289 None => {1290 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12911292 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1293 }1294 }12951296 <PalletEvm<T>>::deposit_log(log.clone());1297 }12981299 set_token_properties(stored_properties);13001301 Ok(())1302 }13031304 1305 1306 1307 1308 1309 1310 pub fn set_allowance_for_all(1311 collection: &CollectionHandle<T>,1312 owner: &T::CrossAccountId,1313 operator: &T::CrossAccountId,1314 approve: bool,1315 set_allowance: impl FnOnce(),1316 log: evm_coder::ethereum::Log,1317 ) -> DispatchResult {1318 if collection.permissions.access() == AccessMode::AllowList {1319 collection.check_allowlist(owner)?;1320 collection.check_allowlist(operator)?;1321 }13221323 Self::ensure_correct_receiver(operator)?;13241325 set_allowance();13261327 <PalletEvm<T>>::deposit_log(log);1328 Self::deposit_event(Event::ApprovedForAll(1329 collection.id,1330 owner.clone(),1331 operator.clone(),1332 approve,1333 ));1334 Ok(())1335 }13361337 1338 1339 1340 1341 1342 pub fn set_collection_property(1343 collection: &CollectionHandle<T>,1344 sender: &T::CrossAccountId,1345 property: Property,1346 ) -> DispatchResult {1347 Self::set_collection_properties(collection, sender, [property].into_iter())1348 }13491350 1351 1352 1353 1354 1355 1356 pub fn set_scoped_collection_property(1357 collection_id: CollectionId,1358 scope: PropertyScope,1359 property: Property,1360 ) -> DispatchResult {1361 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1362 properties.try_scoped_set(scope, property.key, property.value)1363 })1364 .map_err(<Error<T>>::from)?;13651366 Ok(())1367 }13681369 1370 1371 1372 1373 1374 1375 pub fn set_scoped_collection_properties(1376 collection_id: CollectionId,1377 scope: PropertyScope,1378 properties: impl Iterator<Item = Property>,1379 ) -> DispatchResult {1380 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1381 stored_properties.try_scoped_set_from_iter(scope, properties)1382 })1383 .map_err(<Error<T>>::from)?;13841385 Ok(())1386 }13871388 1389 1390 1391 1392 1393 pub fn set_collection_properties(1394 collection: &CollectionHandle<T>,1395 sender: &T::CrossAccountId,1396 properties: impl Iterator<Item = Property>,1397 ) -> DispatchResult {1398 Self::modify_collection_properties(1399 collection,1400 sender,1401 properties.map(|property| (property.key, Some(property.value))),1402 )1403 }14041405 1406 1407 1408 1409 1410 pub fn delete_collection_property(1411 collection: &CollectionHandle<T>,1412 sender: &T::CrossAccountId,1413 property_key: PropertyKey,1414 ) -> DispatchResult {1415 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1416 }14171418 1419 1420 1421 1422 1423 pub fn delete_collection_properties(1424 collection: &CollectionHandle<T>,1425 sender: &T::CrossAccountId,1426 property_keys: impl Iterator<Item = PropertyKey>,1427 ) -> DispatchResult {1428 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1429 }14301431 1432 1433 1434 1435 1436 1437 pub fn set_property_permission_unchecked(1438 collection: CollectionId,1439 property_permission: PropertyKeyPermission,1440 ) -> DispatchResult {1441 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1442 permissions.try_set(property_permission.key, property_permission.permission)1443 })1444 .map_err(<Error<T>>::from)?;1445 Ok(())1446 }14471448 1449 1450 1451 1452 1453 pub fn set_property_permission(1454 collection: &CollectionHandle<T>,1455 sender: &T::CrossAccountId,1456 property_permission: PropertyKeyPermission,1457 ) -> DispatchResult {1458 Self::set_scoped_property_permission(1459 collection,1460 sender,1461 PropertyScope::None,1462 property_permission,1463 )1464 }14651466 1467 1468 1469 1470 1471 1472 pub fn set_scoped_property_permission(1473 collection: &CollectionHandle<T>,1474 sender: &T::CrossAccountId,1475 scope: PropertyScope,1476 property_permission: PropertyKeyPermission,1477 ) -> DispatchResult {1478 collection.check_is_owner_or_admin(sender)?;14791480 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1481 let current_permission = all_permissions.get(&property_permission.key);1482 if matches![1483 current_permission,1484 Some(PropertyPermission { mutable: false, .. })1485 ] {1486 return Err(<Error<T>>::NoPermission.into());1487 }14881489 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1490 let property_permission = property_permission.clone();1491 permissions.try_scoped_set(1492 scope,1493 property_permission.key,1494 property_permission.permission,1495 )1496 })1497 .map_err(<Error<T>>::from)?;14981499 Self::deposit_event(Event::PropertyPermissionSet(1500 collection.id,1501 property_permission.key,1502 ));1503 <PalletEvm<T>>::deposit_log(1504 erc::CollectionHelpersEvents::CollectionChanged {1505 collection_id: eth::collection_id_to_address(collection.id),1506 }1507 .to_log(T::ContractAddress::get()),1508 );15091510 Ok(())1511 }15121513 1514 1515 1516 1517 1518 #[transactional]1519 pub fn set_token_property_permissions(1520 collection: &CollectionHandle<T>,1521 sender: &T::CrossAccountId,1522 property_permissions: Vec<PropertyKeyPermission>,1523 ) -> DispatchResult {1524 Self::set_scoped_token_property_permissions(1525 collection,1526 sender,1527 PropertyScope::None,1528 property_permissions,1529 )1530 }15311532 1533 1534 1535 1536 1537 1538 #[transactional]1539 pub fn set_scoped_token_property_permissions(1540 collection: &CollectionHandle<T>,1541 sender: &T::CrossAccountId,1542 scope: PropertyScope,1543 property_permissions: Vec<PropertyKeyPermission>,1544 ) -> DispatchResult {1545 for prop_pemission in property_permissions {1546 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1547 }15481549 Ok(())1550 }15511552 1553 pub fn get_collection_property(1554 collection_id: CollectionId,1555 key: &PropertyKey,1556 ) -> Option<PropertyValue> {1557 Self::collection_properties(collection_id).get(key).cloned()1558 }15591560 1561 pub fn bytes_keys_to_property_keys(1562 keys: Vec<Vec<u8>>,1563 ) -> Result<Vec<PropertyKey>, DispatchError> {1564 keys.into_iter()1565 .map(|key| -> Result<PropertyKey, DispatchError> {1566 key.try_into()1567 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1568 })1569 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1570 }15711572 1573 pub fn filter_collection_properties(1574 collection_id: CollectionId,1575 keys: Option<Vec<PropertyKey>>,1576 ) -> Result<Vec<Property>, DispatchError> {1577 let properties = Self::collection_properties(collection_id);15781579 let properties = keys1580 .map(|keys| {1581 keys.into_iter()1582 .filter_map(|key| {1583 properties.get(&key).map(|value| Property {1584 key,1585 value: value.clone(),1586 })1587 })1588 .collect()1589 })1590 .unwrap_or_else(|| {1591 properties1592 .into_iter()1593 .map(|(key, value)| Property { key, value })1594 .collect()1595 });15961597 Ok(properties)1598 }15991600 1601 pub fn filter_property_permissions(1602 collection_id: CollectionId,1603 keys: Option<Vec<PropertyKey>>,1604 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1605 let permissions = Self::property_permissions(collection_id);16061607 let key_permissions = keys1608 .map(|keys| {1609 keys.into_iter()1610 .filter_map(|key| {1611 permissions1612 .get(&key)1613 .map(|permission| PropertyKeyPermission {1614 key,1615 permission: permission.clone(),1616 })1617 })1618 .collect()1619 })1620 .unwrap_or_else(|| {1621 permissions1622 .into_iter()1623 .map(|(key, permission)| PropertyKeyPermission { key, permission })1624 .collect()1625 });16261627 Ok(key_permissions)1628 }16291630 1631 1632 1633 pub fn toggle_allowlist(1634 collection: &CollectionHandle<T>,1635 sender: &T::CrossAccountId,1636 user: &T::CrossAccountId,1637 allowed: bool,1638 ) -> DispatchResult {1639 collection.check_is_owner_or_admin(sender)?;16401641 16421643 if allowed {1644 <Allowlist<T>>::insert((collection.id, user), true);1645 Self::deposit_event(Event::<T>::AllowListAddressAdded(1646 collection.id,1647 user.clone(),1648 ));1649 } else {1650 <Allowlist<T>>::remove((collection.id, user));1651 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1652 collection.id,1653 user.clone(),1654 ));1655 }16561657 <PalletEvm<T>>::deposit_log(1658 erc::CollectionHelpersEvents::CollectionChanged {1659 collection_id: eth::collection_id_to_address(collection.id),1660 }1661 .to_log(T::ContractAddress::get()),1662 );16631664 Ok(())1665 }16661667 1668 1669 1670 pub fn toggle_admin(1671 collection: &CollectionHandle<T>,1672 sender: &T::CrossAccountId,1673 user: &T::CrossAccountId,1674 admin: bool,1675 ) -> DispatchResult {1676 collection.check_is_internal()?;1677 collection.check_is_owner(sender)?;16781679 let is_admin = <IsAdmin<T>>::get((collection.id, user));1680 if is_admin == admin {1681 if admin {1682 return Ok(());1683 } else {1684 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1685 }1686 }1687 let amount = <AdminAmount<T>>::get(collection.id);16881689 16901691 if admin {1692 let amount = amount1693 .checked_add(1)1694 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1695 ensure!(1696 amount <= Self::collection_admins_limit(),1697 <Error<T>>::CollectionAdminCountExceeded,1698 );16991700 <AdminAmount<T>>::insert(collection.id, amount);1701 <IsAdmin<T>>::insert((collection.id, user), true);17021703 Self::deposit_event(Event::<T>::CollectionAdminAdded(1704 collection.id,1705 user.clone(),1706 ));1707 } else {1708 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1709 <IsAdmin<T>>::remove((collection.id, user));17101711 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1712 collection.id,1713 user.clone(),1714 ));1715 }17161717 <PalletEvm<T>>::deposit_log(1718 erc::CollectionHelpersEvents::CollectionChanged {1719 collection_id: eth::collection_id_to_address(collection.id),1720 }1721 .to_log(T::ContractAddress::get()),1722 );17231724 Ok(())1725 }17261727 1728 pub fn update_limits(1729 user: &T::CrossAccountId,1730 collection: &mut CollectionHandle<T>,1731 new_limit: CollectionLimits,1732 ) -> DispatchResult {1733 collection.check_is_internal()?;1734 collection.check_is_owner_or_admin(user)?;17351736 collection.limits =1737 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;17381739 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1740 <PalletEvm<T>>::deposit_log(1741 erc::CollectionHelpersEvents::CollectionChanged {1742 collection_id: eth::collection_id_to_address(collection.id),1743 }1744 .to_log(T::ContractAddress::get()),1745 );17461747 collection.save()1748 }17491750 1751 fn clamp_limits(1752 mode: CollectionMode,1753 old_limit: &CollectionLimits,1754 mut new_limit: CollectionLimits,1755 ) -> Result<CollectionLimits, DispatchError> {1756 let limits = old_limit;1757 limit_default!(old_limit, new_limit,1758 account_token_ownership_limit => ensure!(1759 new_limit <= MAX_TOKEN_OWNERSHIP,1760 <Error<T>>::CollectionLimitBoundsExceeded,1761 ),1762 sponsored_data_size => ensure!(1763 new_limit <= CUSTOM_DATA_LIMIT,1764 <Error<T>>::CollectionLimitBoundsExceeded,1765 ),17661767 sponsored_data_rate_limit => {},1768 token_limit => ensure!(1769 old_limit >= new_limit && new_limit > 0,1770 <Error<T>>::CollectionTokenLimitExceeded1771 ),17721773 sponsor_transfer_timeout(match mode {1774 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1775 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1776 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1777 }) => ensure!(1778 new_limit <= MAX_SPONSOR_TIMEOUT,1779 <Error<T>>::CollectionLimitBoundsExceeded,1780 ),1781 sponsor_approve_timeout => {},1782 owner_can_transfer => ensure!(1783 !limits.owner_can_transfer_instaled() ||1784 old_limit || !new_limit,1785 <Error<T>>::OwnerPermissionsCantBeReverted,1786 ),1787 owner_can_destroy => ensure!(1788 old_limit || !new_limit,1789 <Error<T>>::OwnerPermissionsCantBeReverted,1790 ),1791 transfers_enabled => {},1792 );1793 Ok(new_limit)1794 }17951796 1797 pub fn update_permissions(1798 user: &T::CrossAccountId,1799 collection: &mut CollectionHandle<T>,1800 new_permission: CollectionPermissions,1801 ) -> DispatchResult {1802 collection.check_is_internal()?;1803 collection.check_is_owner_or_admin(user)?;1804 collection.permissions = Self::clamp_permissions(1805 collection.mode.clone(),1806 &collection.permissions,1807 new_permission,1808 )?;18091810 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1811 <PalletEvm<T>>::deposit_log(1812 erc::CollectionHelpersEvents::CollectionChanged {1813 collection_id: eth::collection_id_to_address(collection.id),1814 }1815 .to_log(T::ContractAddress::get()),1816 );18171818 collection.save()1819 }18201821 1822 fn clamp_permissions(1823 _mode: CollectionMode,1824 old_permission: &CollectionPermissions,1825 mut new_permission: CollectionPermissions,1826 ) -> Result<CollectionPermissions, DispatchError> {1827 limit_default_clone!(old_permission, new_permission,1828 access => {},1829 mint_mode => {},1830 nesting => { },1831 );1832 Ok(new_permission)1833 }18341835 1836 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1837 CollectionProperties::<T>::mutate(collection_id, |properties| {1838 properties.recompute_consumed_space();1839 });18401841 Ok(())1842 }1843}184418451846#[macro_export]1847macro_rules! unsupported {1848 ($runtime:path) => {1849 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1850 };1851}185218531854pub trait CommonWeightInfo<CrossAccountId> {1855 1856 fn create_item(data: &CreateItemData) -> Weight {1857 Self::create_multiple_items(from_ref(data))1858 }18591860 1861 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;18621863 1864 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;18651866 1867 fn burn_item() -> Weight;18681869 1870 1871 1872 fn set_collection_properties(amount: u32) -> Weight;18731874 1875 1876 1877 fn delete_collection_properties(amount: u32) -> Weight;18781879 1880 1881 1882 fn set_token_properties(amount: u32) -> Weight;18831884 1885 1886 1887 fn delete_token_properties(amount: u32) -> Weight;18881889 1890 1891 1892 fn set_token_property_permissions(amount: u32) -> Weight;18931894 1895 fn transfer() -> Weight;18961897 1898 fn approve() -> Weight;18991900 1901 fn approve_from() -> Weight;19021903 1904 fn transfer_from() -> Weight;19051906 1907 fn burn_from() -> Weight;19081909 1910 1911 1912 1913 fn burn_recursively_self_raw() -> Weight;19141915 1916 1917 1918 fn burn_recursively_breadth_raw(amount: u32) -> Weight;19191920 1921 1922 1923 1924 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1925 Self::burn_recursively_self_raw()1926 .saturating_mul(max_selfs.max(1) as u64)1927 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1928 }19291930 1931 fn token_owner() -> Weight;19321933 1934 fn set_allowance_for_all() -> Weight;19351936 1937 fn force_repair_item() -> Weight;1938}193919401941pub trait RefungibleExtensionsWeightInfo {1942 1943 fn repartition() -> Weight;1944}194519461947194819491950pub trait CommonCollectionOperations<T: Config> {1951 1952 1953 1954 1955 1956 1957 fn create_item(1958 &self,1959 sender: T::CrossAccountId,1960 to: T::CrossAccountId,1961 data: CreateItemData,1962 nesting_budget: &dyn Budget,1963 ) -> DispatchResultWithPostInfo;19641965 1966 1967 1968 1969 1970 1971 fn create_multiple_items(1972 &self,1973 sender: T::CrossAccountId,1974 to: T::CrossAccountId,1975 data: Vec<CreateItemData>,1976 nesting_budget: &dyn Budget,1977 ) -> DispatchResultWithPostInfo;19781979 1980 1981 1982 1983 1984 1985 fn create_multiple_items_ex(1986 &self,1987 sender: T::CrossAccountId,1988 data: CreateItemExData<T::CrossAccountId>,1989 nesting_budget: &dyn Budget,1990 ) -> DispatchResultWithPostInfo;19911992 1993 1994 1995 1996 1997 fn burn_item(1998 &self,1999 sender: T::CrossAccountId,2000 token: TokenId,2001 amount: u128,2002 ) -> DispatchResultWithPostInfo;20032004 2005 2006 2007 2008 2009 2010 fn burn_item_recursively(2011 &self,2012 sender: T::CrossAccountId,2013 token: TokenId,2014 self_budget: &dyn Budget,2015 breadth_budget: &dyn Budget,2016 ) -> DispatchResultWithPostInfo;20172018 2019 2020 2021 2022 fn set_collection_properties(2023 &self,2024 sender: T::CrossAccountId,2025 properties: Vec<Property>,2026 ) -> DispatchResultWithPostInfo;20272028 2029 2030 2031 2032 fn delete_collection_properties(2033 &self,2034 sender: &T::CrossAccountId,2035 property_keys: Vec<PropertyKey>,2036 ) -> DispatchResultWithPostInfo;20372038 2039 2040 2041 2042 2043 2044 2045 2046 2047 fn set_token_properties(2048 &self,2049 sender: T::CrossAccountId,2050 token_id: TokenId,2051 properties: Vec<Property>,2052 budget: &dyn Budget,2053 ) -> DispatchResultWithPostInfo;20542055 2056 2057 2058 2059 2060 2061 2062 2063 2064 fn delete_token_properties(2065 &self,2066 sender: T::CrossAccountId,2067 token_id: TokenId,2068 property_keys: Vec<PropertyKey>,2069 budget: &dyn Budget,2070 ) -> DispatchResultWithPostInfo;20712072 2073 2074 2075 2076 2077 2078 fn set_token_property_permissions(2079 &self,2080 sender: &T::CrossAccountId,2081 property_permissions: Vec<PropertyKeyPermission>,2082 ) -> DispatchResultWithPostInfo;20832084 2085 2086 2087 2088 2089 2090 2091 fn transfer(2092 &self,2093 sender: T::CrossAccountId,2094 to: T::CrossAccountId,2095 token: TokenId,2096 amount: u128,2097 budget: &dyn Budget,2098 ) -> DispatchResultWithPostInfo;20992100 2101 2102 2103 2104 2105 2106 fn approve(2107 &self,2108 sender: T::CrossAccountId,2109 spender: T::CrossAccountId,2110 token: TokenId,2111 amount: u128,2112 ) -> DispatchResultWithPostInfo;21132114 2115 2116 2117 2118 2119 2120 2121 fn approve_from(2122 &self,2123 sender: T::CrossAccountId,2124 from: T::CrossAccountId,2125 to: T::CrossAccountId,2126 token: TokenId,2127 amount: u128,2128 ) -> DispatchResultWithPostInfo;21292130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 fn transfer_from(2141 &self,2142 sender: T::CrossAccountId,2143 from: T::CrossAccountId,2144 to: T::CrossAccountId,2145 token: TokenId,2146 amount: u128,2147 budget: &dyn Budget,2148 ) -> DispatchResultWithPostInfo;21492150 2151 2152 2153 2154 2155 2156 2157 2158 2159 fn burn_from(2160 &self,2161 sender: T::CrossAccountId,2162 from: T::CrossAccountId,2163 token: TokenId,2164 amount: u128,2165 budget: &dyn Budget,2166 ) -> DispatchResultWithPostInfo;21672168 2169 2170 2171 2172 2173 2174 fn check_nesting(2175 &self,2176 sender: T::CrossAccountId,2177 from: (CollectionId, TokenId),2178 under: TokenId,2179 budget: &dyn Budget,2180 ) -> DispatchResult;21812182 2183 2184 2185 2186 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21872188 2189 2190 2191 2192 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21932194 2195 2196 2197 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21982199 2200 fn collection_tokens(&self) -> Vec<TokenId>;22012202 2203 2204 2205 fn token_exists(&self, token: TokenId) -> bool;22062207 2208 fn last_token_id(&self) -> TokenId;22092210 2211 2212 2213 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22142215 2216 2217 2218 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22192220 2221 2222 2223 2224 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22252226 2227 2228 2229 2230 2231 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22322233 2234 fn total_supply(&self) -> u32;22352236 2237 2238 2239 fn account_balance(&self, account: T::CrossAccountId) -> u32;22402241 2242 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;22432244 2245 fn total_pieces(&self, token: TokenId) -> Option<u128>;22462247 2248 2249 2250 2251 2252 fn allowance(2253 &self,2254 sender: T::CrossAccountId,2255 spender: T::CrossAccountId,2256 token: TokenId,2257 ) -> u128;22582259 2260 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;22612262 2263 2264 2265 2266 fn set_allowance_for_all(2267 &self,2268 owner: T::CrossAccountId,2269 operator: T::CrossAccountId,2270 approve: bool,2271 ) -> DispatchResultWithPostInfo;22722273 2274 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;22752276 2277 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2278}227922802281pub trait RefungibleExtensions<T>2282where2283 T: Config,2284{2285 2286 2287 2288 2289 2290 2291 2292 fn repartition(2293 &self,2294 sender: &T::CrossAccountId,2295 token: TokenId,2296 amount: u128,2297 ) -> DispatchResultWithPostInfo;2298}22992300230123022303pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2304 let post_info = PostDispatchInfo {2305 actual_weight: Some(weight),2306 pays_fee: Pays::Yes,2307 };2308 match res {2309 Ok(()) => Ok(post_info),2310 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2311 }2312}23132314impl<T: Config> From<PropertiesError> for Error<T> {2315 fn from(error: PropertiesError) -> Self {2316 match error {2317 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2318 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2319 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2320 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2321 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2322 }2323 }2324}