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 pallet_evm::GasWeightMapping;72use up_data_structs::{73 AccessMode, COLLECTION_NUMBER_LIMIT, Collection, RpcCollection, CollectionFlags,74 RpcCollectionFlags, CollectionId, CreateItemData, MAX_TOKEN_PREFIX_LENGTH,75 COLLECTION_ADMINS_LIMIT, TokenId, TokenChild, CollectionStats, MAX_TOKEN_OWNERSHIP,76 CollectionMode, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,77 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,78 CreateCollectionData, SponsorshipState, CreateItemExData, SponsoringRateLimit, budget::Budget,79 PhantomType, Property, CollectionProperties as CollectionPropertiesT, TokenProperties,80 PropertiesPermissionMap, PropertyKey, PropertyValue, PropertyPermission, PropertiesError,81 TokenOwnerError, PropertyKeyPermission, TokenData, TrySetProperty, PropertyScope,82 CollectionPermissions,83};84use up_pov_estimate_rpc::PovInfo;8586pub use pallet::*;87use sp_core::H160;88use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};8990#[cfg(feature = "runtime-benchmarks")]91pub mod benchmarking;92pub mod dispatch;93pub mod erc;94pub mod eth;95pub mod helpers;96#[allow(missing_docs)]97pub mod weights;9899pub type SelfWeightOf<T> = <T as Config>::WeightInfo;100101102103104105106107#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]108pub struct CollectionHandle<T: Config> {109 110 pub id: CollectionId,111 collection: Collection<T::AccountId>,112 113 pub recorder: SubstrateRecorder<T>,114}115116impl<T: Config> WithRecorder<T> for CollectionHandle<T> {117 fn recorder(&self) -> &SubstrateRecorder<T> {118 &self.recorder119 }120 fn into_recorder(self) -> SubstrateRecorder<T> {121 self.recorder122 }123}124125impl<T: Config> CollectionHandle<T> {126 127 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {128 Self::new_with_recorder(id, SubstrateRecorder::new(gas_limit))129 }130131 132 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {133 <CollectionById<T>>::get(id).map(|collection| Self {134 id,135 collection,136 recorder,137 })138 }139140 141 142 pub fn new(id: CollectionId) -> Option<Self> {143 Self::new_with_gas_limit(id, u64::MAX)144 }145146 147 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {148 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)149 }150151 152 pub fn consume_store_reads(153 &self,154 reads: u64,155 ) -> pallet_evm_coder_substrate::execution::Result<()> {156 self.recorder157 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(158 <T as frame_system::Config>::DbWeight::get()159 .read160 .saturating_mul(reads),161 162 0,163 )))164 }165166 167 pub fn consume_store_writes(168 &self,169 writes: u64,170 ) -> pallet_evm_coder_substrate::execution::Result<()> {171 self.recorder172 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(173 <T as frame_system::Config>::DbWeight::get()174 .write175 .saturating_mul(writes),176 177 0,178 )))179 }180181 182 pub fn consume_store_reads_and_writes(183 &self,184 reads: u64,185 writes: u64,186 ) -> pallet_evm_coder_substrate::execution::Result<()> {187 let weight = <T as frame_system::Config>::DbWeight::get();188 let reads = weight.read.saturating_mul(reads);189 let writes = weight.read.saturating_mul(writes);190 self.recorder191 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(192 reads.saturating_add(writes),193 194 0,195 )))196 }197198 199 pub fn save(&self) -> DispatchResult {200 <CollectionById<T>>::insert(self.id, &self.collection);201 Ok(())202 }203204 205 206 207 208 209 pub fn set_sponsor(210 &mut self,211 sender: &T::CrossAccountId,212 sponsor: T::AccountId,213 ) -> DispatchResult {214 self.check_is_internal()?;215 self.check_is_owner_or_admin(sender)?;216217 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());218219 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));220 <PalletEvm<T>>::deposit_log(221 erc::CollectionHelpersEvents::CollectionChanged {222 collection_id: eth::collection_id_to_address(self.id),223 }224 .to_log(T::ContractAddress::get()),225 );226227 self.save()228 }229230 231 232 233 234 235 236 237 238 239 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {240 self.check_is_internal()?;241242 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());243244 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));245 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));246 <PalletEvm<T>>::deposit_log(247 erc::CollectionHelpersEvents::CollectionChanged {248 collection_id: eth::collection_id_to_address(self.id),249 }250 .to_log(T::ContractAddress::get()),251 );252253 self.save()254 }255256 257 258 259 260 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {261 self.check_is_internal()?;262 ensure!(263 self.collection.sponsorship.pending_sponsor() == Some(sender),264 Error::<T>::ConfirmSponsorshipFail265 );266267 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());268269 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));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 );276277 self.save()278 }279280 281 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {282 self.check_is_internal()?;283 self.check_is_owner_or_admin(sender)?;284285 self.collection.sponsorship = SponsorshipState::Disabled;286287 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));288 <PalletEvm<T>>::deposit_log(289 erc::CollectionHelpersEvents::CollectionChanged {290 collection_id: eth::collection_id_to_address(self.id),291 }292 .to_log(T::ContractAddress::get()),293 );294 self.save()295 }296297 298 299 300 301 pub fn force_remove_sponsor(&mut self) -> DispatchResult {302 self.check_is_internal()?;303304 self.collection.sponsorship = SponsorshipState::Disabled;305306 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));307 <PalletEvm<T>>::deposit_log(308 erc::CollectionHelpersEvents::CollectionChanged {309 collection_id: eth::collection_id_to_address(self.id),310 }311 .to_log(T::ContractAddress::get()),312 );313 self.save()314 }315316 317 318 pub fn check_is_internal(&self) -> DispatchResult {319 if self.flags.external {320 return Err(<Error<T>>::CollectionIsExternal)?;321 }322323 Ok(())324 }325326 327 328 pub fn check_is_external(&self) -> DispatchResult {329 if !self.flags.external {330 return Err(<Error<T>>::CollectionIsInternal)?;331 }332333 Ok(())334 }335}336337impl<T: Config> Deref for CollectionHandle<T> {338 type Target = Collection<T::AccountId>;339340 fn deref(&self) -> &Self::Target {341 &self.collection342 }343}344345impl<T: Config> DerefMut for CollectionHandle<T> {346 fn deref_mut(&mut self) -> &mut Self::Target {347 &mut self.collection348 }349}350351impl<T: Config> CollectionHandle<T> {352 353 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {354 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);355 Ok(())356 }357358 359 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {360 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))361 }362363 364 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {365 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);366 Ok(())367 }368369 370 371 372 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {373 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)374 }375376 377 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {378 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)379 }380381 382 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {383 ensure!(384 <Allowlist<T>>::get((self.id, user)),385 <Error<T>>::AddressNotInAllowlist386 );387 Ok(())388 }389390 391 392 393 pub fn change_owner(394 &mut self,395 caller: T::CrossAccountId,396 new_owner: T::CrossAccountId,397 ) -> DispatchResult {398 self.check_is_internal()?;399 self.check_is_owner(&caller)?;400 self.collection.owner = new_owner.as_sub().clone();401402 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(403 self.id,404 new_owner.as_sub().clone(),405 ));406 <PalletEvm<T>>::deposit_log(407 erc::CollectionHelpersEvents::CollectionChanged {408 collection_id: eth::collection_id_to_address(self.id),409 }410 .to_log(T::ContractAddress::get()),411 );412413 self.save()414 }415}416417#[frame_support::pallet]418pub mod pallet {419420 use super::*;421 use dispatch::CollectionDispatch;422 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};423 use frame_support::traits::Currency;424 use up_data_structs::{TokenId, mapping::TokenAddressMapping};425 use scale_info::TypeInfo;426 use weights::WeightInfo;427428 #[pallet::config]429 pub trait Config:430 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo431 {432 433 type WeightInfo: WeightInfo;434435 436 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;437438 439 type Currency: Currency<Self::AccountId>;440441 442 #[pallet::constant]443 type CollectionCreationPrice: Get<444 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,445 >;446447 448 type CollectionDispatch: CollectionDispatch<Self>;449450 451 type TreasuryAccountId: Get<Self::AccountId>;452453 454 #[pallet::constant]455 type ContractAddress: Get<H160>;456457 458 type EvmTokenAddressMapping: TokenAddressMapping<H160>;459460 461 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;462 }463464 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);465 pub const NATIVE_FINGIBLE_COLLECTION_ID: CollectionId = CollectionId(0);466467 #[pallet::pallet]468 #[pallet::storage_version(STORAGE_VERSION)]469 pub struct Pallet<T>(_);470471 #[pallet::extra_constants]472 impl<T: Config> Pallet<T> {473 474 pub fn collection_admins_limit() -> u32 {475 COLLECTION_ADMINS_LIMIT476 }477 }478479 #[pallet::genesis_config]480 pub struct GenesisConfig<T>(PhantomData<T>);481482 #[cfg(feature = "std")]483 impl<T: Config> Default for GenesisConfig<T> {484 fn default() -> Self {485 Self(Default::default())486 }487 }488489 #[pallet::genesis_build]490 impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {491 fn build(&self) {492 StorageVersion::new(1).put::<Pallet<T>>();493 }494 }495496 impl<T: Config> Pallet<T> {497 498 pub fn deposit_event(event: Event<T>) {499 let event = <T as Config>::RuntimeEvent::from(event);500 let event = event.into();501 <frame_system::Pallet<T>>::deposit_event(event)502 }503 }504505 #[pallet::event]506 pub enum Event<T: Config> {507 508 CollectionCreated(509 510 CollectionId,511 512 u8,513 514 T::AccountId,515 ),516517 518 CollectionDestroyed(519 520 CollectionId,521 ),522523 524 ItemCreated(525 526 CollectionId,527 528 TokenId,529 530 T::CrossAccountId,531 532 u128,533 ),534535 536 ItemDestroyed(537 538 CollectionId,539 540 TokenId,541 542 T::CrossAccountId,543 544 u128,545 ),546547 548 Transfer(549 550 CollectionId,551 552 TokenId,553 554 T::CrossAccountId,555 556 T::CrossAccountId,557 558 u128,559 ),560561 562 Approved(563 564 CollectionId,565 566 TokenId,567 568 T::CrossAccountId,569 570 T::CrossAccountId,571 572 u128,573 ),574575 576 ApprovedForAll(577 578 CollectionId,579 580 T::CrossAccountId,581 582 T::CrossAccountId,583 584 bool,585 ),586587 588 CollectionPropertySet(589 590 CollectionId,591 592 PropertyKey,593 ),594595 596 CollectionPropertyDeleted(597 598 CollectionId,599 600 PropertyKey,601 ),602603 604 TokenPropertySet(605 606 CollectionId,607 608 TokenId,609 610 PropertyKey,611 ),612613 614 TokenPropertyDeleted(615 616 CollectionId,617 618 TokenId,619 620 PropertyKey,621 ),622623 624 PropertyPermissionSet(625 626 CollectionId,627 628 PropertyKey,629 ),630631 632 AllowListAddressAdded(633 634 CollectionId,635 636 T::CrossAccountId,637 ),638639 640 AllowListAddressRemoved(641 642 CollectionId,643 644 T::CrossAccountId,645 ),646647 648 CollectionAdminAdded(649 650 CollectionId,651 652 T::CrossAccountId,653 ),654655 656 CollectionAdminRemoved(657 658 CollectionId,659 660 T::CrossAccountId,661 ),662663 664 CollectionLimitSet(665 666 CollectionId,667 ),668669 670 CollectionOwnerChanged(671 672 CollectionId,673 674 T::AccountId,675 ),676677 678 CollectionPermissionSet(679 680 CollectionId,681 ),682683 684 CollectionSponsorSet(685 686 CollectionId,687 688 T::AccountId,689 ),690691 692 SponsorshipConfirmed(693 694 CollectionId,695 696 T::AccountId,697 ),698699 700 CollectionSponsorRemoved(701 702 CollectionId,703 ),704 }705706 #[pallet::error]707 pub enum Error<T> {708 709 CollectionNotFound,710 711 MustBeTokenOwner,712 713 NoPermission,714 715 CantDestroyNotEmptyCollection,716 717 PublicMintingNotAllowed,718 719 AddressNotInAllowlist,720721 722 CollectionNameLimitExceeded,723 724 CollectionDescriptionLimitExceeded,725 726 CollectionTokenPrefixLimitExceeded,727 728 TotalCollectionsLimitExceeded,729 730 CollectionAdminCountExceeded,731 732 CollectionLimitBoundsExceeded,733 734 OwnerPermissionsCantBeReverted,735 736 TransferNotAllowed,737 738 AccountTokenLimitExceeded,739 740 CollectionTokenLimitExceeded,741 742 MetadataFlagFrozen,743744 745 TokenNotFound,746 747 TokenValueTooLow,748 749 ApprovedValueTooLow,750 751 CantApproveMoreThanOwned,752 753 AddressIsNotEthMirror,754755 756 AddressIsZero,757758 759 UnsupportedOperation,760761 762 NotSufficientFounds,763764 765 UserIsNotAllowedToNest,766 767 SourceCollectionIsNotAllowedToNest,768769 770 CollectionFieldSizeExceeded,771772 773 NoSpaceForProperty,774775 776 PropertyLimitReached,777778 779 PropertyKeyIsTooLong,780781 782 InvalidCharacterInPropertyKey,783784 785 EmptyPropertyKey,786787 788 CollectionIsExternal,789790 791 CollectionIsInternal,792793 794 ConfirmSponsorshipFail,795796 797 UserIsNotCollectionAdmin,798 }799800 801 #[pallet::storage]802 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;803804 805 #[pallet::storage]806 pub type DestroyedCollectionCount<T> =807 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;808809 810 #[pallet::storage]811 pub type CollectionById<T> = StorageMap<812 Hasher = Blake2_128Concat,813 Key = CollectionId,814 Value = Collection<<T as frame_system::Config>::AccountId>,815 QueryKind = OptionQuery,816 >;817818 819 #[pallet::storage]820 #[pallet::getter(fn collection_properties)]821 pub type CollectionProperties<T> = StorageMap<822 Hasher = Blake2_128Concat,823 Key = CollectionId,824 Value = CollectionPropertiesT,825 QueryKind = ValueQuery,826 >;827828 829 #[pallet::storage]830 #[pallet::getter(fn property_permissions)]831 pub type CollectionPropertyPermissions<T> = StorageMap<832 Hasher = Blake2_128Concat,833 Key = CollectionId,834 Value = PropertiesPermissionMap,835 QueryKind = ValueQuery,836 >;837838 839 #[pallet::storage]840 pub type AdminAmount<T> = StorageMap<841 Hasher = Blake2_128Concat,842 Key = CollectionId,843 Value = u32,844 QueryKind = ValueQuery,845 >;846847 848 #[pallet::storage]849 pub type IsAdmin<T: Config> = StorageNMap<850 Key = (851 Key<Blake2_128Concat, CollectionId>,852 Key<Blake2_128Concat, T::CrossAccountId>,853 ),854 Value = bool,855 QueryKind = ValueQuery,856 >;857858 859 #[pallet::storage]860 pub type Allowlist<T: Config> = StorageNMap<861 Key = (862 Key<Blake2_128Concat, CollectionId>,863 Key<Blake2_128Concat, T::CrossAccountId>,864 ),865 Value = bool,866 QueryKind = ValueQuery,867 >;868869 870 #[pallet::storage]871 pub type DummyStorageValue<T: Config> = StorageValue<872 Value = (873 CollectionStats,874 CollectionId,875 TokenId,876 TokenChild,877 PhantomType<(878 TokenData<T::CrossAccountId>,879 RpcCollection<T::AccountId>,880 881 PovInfo,882 )>,883 ),884 QueryKind = OptionQuery,885 >;886}887888impl<T: Config> Pallet<T> {889 890 891 892 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {893 ensure!(894 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,895 <Error<T>>::AddressIsZero896 );897 Ok(())898 }899900 901 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {902 <IsAdmin<T>>::iter_prefix((collection,))903 .map(|(a, _)| a)904 .collect()905 }906907 908 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {909 <Allowlist<T>>::iter_prefix((collection,))910 .map(|(a, _)| a)911 .collect()912 }913914 915 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {916 <Allowlist<T>>::get((collection, user))917 }918919 920 pub fn collection_stats() -> CollectionStats {921 let created = <CreatedCollectionCount<T>>::get();922 let destroyed = <DestroyedCollectionCount<T>>::get();923 CollectionStats {924 created: created.0,925 destroyed: destroyed.0,926 alive: created.0 - destroyed.0,927 }928 }929930 931 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {932 let collection = <CollectionById<T>>::get(collection)?;933 let limits = collection.limits;934 let effective_limits = CollectionLimits {935 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),936 sponsored_data_size: Some(limits.sponsored_data_size()),937 sponsored_data_rate_limit: Some(938 limits939 .sponsored_data_rate_limit940 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),941 ),942 token_limit: Some(limits.token_limit()),943 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(944 match collection.mode {945 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,946 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,947 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,948 },949 )),950 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),951 owner_can_transfer: Some(limits.owner_can_transfer()),952 owner_can_destroy: Some(limits.owner_can_destroy()),953 transfers_enabled: Some(limits.transfers_enabled()),954 };955956 Some(effective_limits)957 }958959 960 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {961 let Collection {962 name,963 description,964 owner,965 mode,966 token_prefix,967 sponsorship,968 limits,969 permissions,970 flags,971 } = <CollectionById<T>>::get(collection)?;972973 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)974 .into_iter()975 .map(|(key, permission)| PropertyKeyPermission { key, permission })976 .collect();977978 let properties = <CollectionProperties<T>>::get(collection)979 .into_iter()980 .map(|(key, value)| Property { key, value })981 .collect();982983 let permissions = CollectionPermissions {984 access: Some(permissions.access()),985 mint_mode: Some(permissions.mint_mode()),986 nesting: Some(permissions.nesting().clone()),987 };988989 Some(RpcCollection {990 name: name.into_inner(),991 description: description.into_inner(),992 owner,993 mode,994 token_prefix: token_prefix.into_inner(),995 sponsorship,996 limits,997 permissions,998 token_property_permissions,999 properties,1000 read_only: flags.external,10011002 flags: RpcCollectionFlags {1003 foreign: flags.foreign,1004 erc721metadata: flags.erc721metadata,1005 },1006 })1007 }1008}10091010macro_rules! limit_default {1011 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1012 $(1013 if let Some($new) = $new.$field {1014 let $old = $old.$field($($arg)?);1015 let _ = $new;1016 let _ = $old;1017 $check1018 } else {1019 $new.$field = $old.$field1020 }1021 )*1022 }};1023}1024macro_rules! limit_default_clone {1025 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1026 $(1027 if let Some($new) = $new.$field.clone() {1028 let $old = $old.$field($($arg)?);1029 let _ = $new;1030 let _ = $old;1031 $check1032 } else {1033 $new.$field = $old.$field.clone()1034 }1035 )*1036 }};1037}10381039impl<T: Config> Pallet<T> {1040 1041 1042 1043 1044 1045 pub fn init_collection(1046 owner: T::CrossAccountId,1047 payer: T::CrossAccountId,1048 data: CreateCollectionData<T::AccountId>,1049 flags: CollectionFlags,1050 ) -> Result<CollectionId, DispatchError> {1051 {1052 ensure!(1053 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1054 Error::<T>::CollectionTokenPrefixLimitExceeded1055 );1056 }10571058 let created_count = <CreatedCollectionCount<T>>::get()1059 .01060 .checked_add(1)1061 .ok_or(ArithmeticError::Overflow)?;1062 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1063 let id = CollectionId(created_count);10641065 1066 ensure!(1067 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1068 <Error<T>>::TotalCollectionsLimitExceeded1069 );10701071 10721073 let collection = Collection {1074 owner: owner.as_sub().clone(),1075 name: data.name,1076 mode: data.mode.clone(),1077 description: data.description,1078 token_prefix: data.token_prefix,1079 sponsorship: data1080 .pending_sponsor1081 .map(SponsorshipState::Unconfirmed)1082 .unwrap_or_default(),1083 limits: data1084 .limits1085 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1086 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1087 permissions: data1088 .permissions1089 .map(|permissions| {1090 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1091 })1092 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1093 flags,1094 };10951096 let mut collection_properties = CollectionPropertiesT::new();1097 collection_properties1098 .try_set_from_iter(data.properties.into_iter())1099 .map_err(<Error<T>>::from)?;11001101 CollectionProperties::<T>::insert(id, collection_properties);11021103 let mut token_props_permissions = PropertiesPermissionMap::new();1104 token_props_permissions1105 .try_set_from_iter(data.token_property_permissions.into_iter())1106 .map_err(<Error<T>>::from)?;11071108 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11091110 1111 {1112 let mut imbalance =1113 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1114 imbalance.subsume(1115 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1116 &T::TreasuryAccountId::get(),1117 T::CollectionCreationPrice::get(),1118 ),1119 );1120 <T as Config>::Currency::settle(1121 payer.as_sub(),1122 imbalance,1123 WithdrawReasons::TRANSFER,1124 ExistenceRequirement::KeepAlive,1125 )1126 .map_err(|_| Error::<T>::NotSufficientFounds)?;1127 }11281129 <CreatedCollectionCount<T>>::put(created_count);1130 <Pallet<T>>::deposit_event(Event::CollectionCreated(1131 id,1132 data.mode.id(),1133 owner.as_sub().clone(),1134 ));1135 <PalletEvm<T>>::deposit_log(1136 erc::CollectionHelpersEvents::CollectionCreated {1137 owner: *owner.as_eth(),1138 collection_id: eth::collection_id_to_address(id),1139 }1140 .to_log(T::ContractAddress::get()),1141 );1142 <CollectionById<T>>::insert(id, collection);1143 Ok(id)1144 }11451146 1147 1148 1149 1150 pub fn destroy_collection(1151 collection: CollectionHandle<T>,1152 sender: &T::CrossAccountId,1153 ) -> DispatchResult {1154 ensure!(1155 collection.limits.owner_can_destroy(),1156 <Error<T>>::NoPermission,1157 );1158 collection.check_is_owner(sender)?;11591160 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1161 .01162 .checked_add(1)1163 .ok_or(ArithmeticError::Overflow)?;11641165 11661167 <DestroyedCollectionCount<T>>::put(destroyed_collections);1168 <CollectionById<T>>::remove(collection.id);1169 <AdminAmount<T>>::remove(collection.id);1170 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1171 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1172 <CollectionProperties<T>>::remove(collection.id);11731174 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11751176 <PalletEvm<T>>::deposit_log(1177 erc::CollectionHelpersEvents::CollectionDestroyed {1178 collection_id: eth::collection_id_to_address(collection.id),1179 }1180 .to_log(T::ContractAddress::get()),1181 );1182 Ok(())1183 }11841185 1186 1187 1188 1189 1190 1191 1192 1193 #[transactional]1194 fn modify_collection_properties(1195 collection: &CollectionHandle<T>,1196 sender: &T::CrossAccountId,1197 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1198 ) -> DispatchResult {1199 collection.check_is_owner_or_admin(sender)?;12001201 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12021203 for (key, value) in properties_updates {1204 match value {1205 Some(value) => {1206 stored_properties1207 .try_set(key.clone(), value)1208 .map_err(<Error<T>>::from)?;12091210 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1211 <PalletEvm<T>>::deposit_log(1212 erc::CollectionHelpersEvents::CollectionChanged {1213 collection_id: eth::collection_id_to_address(collection.id),1214 }1215 .to_log(T::ContractAddress::get()),1216 );1217 }1218 None => {1219 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12201221 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1222 <PalletEvm<T>>::deposit_log(1223 erc::CollectionHelpersEvents::CollectionChanged {1224 collection_id: eth::collection_id_to_address(collection.id),1225 }1226 .to_log(T::ContractAddress::get()),1227 );1228 }1229 }1230 }12311232 <CollectionProperties<T>>::set(collection.id, stored_properties);12331234 Ok(())1235 }12361237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 pub fn modify_token_properties(1255 collection: &CollectionHandle<T>,1256 sender: &T::CrossAccountId,1257 token_id: TokenId,1258 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1259 is_token_create: bool,1260 mut stored_properties: TokenProperties,1261 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1262 set_token_properties: impl FnOnce(TokenProperties),1263 log: evm_coder::ethereum::Log,1264 ) -> DispatchResult {1265 let is_collection_admin = collection.is_owner_or_admin(sender);1266 let permissions = Self::property_permissions(collection.id);12671268 let mut token_owner_result = None;1269 let mut is_token_owner = || -> Result<bool, DispatchError> {1270 *token_owner_result.get_or_insert_with(&is_token_owner)1271 };12721273 for (key, value) in properties_updates {1274 let permission = permissions1275 .get(&key)1276 .cloned()1277 .unwrap_or_else(PropertyPermission::none);12781279 let is_property_exists = stored_properties.get(&key).is_some();12801281 match permission {1282 PropertyPermission { mutable: false, .. } if is_property_exists => {1283 return Err(<Error<T>>::NoPermission.into());1284 }12851286 PropertyPermission {1287 collection_admin,1288 token_owner,1289 ..1290 } => {1291 1292 let is_token_create =1293 is_token_create && (collection_admin || token_owner) && value.is_some();1294 if !(is_token_create1295 || (collection_admin && is_collection_admin)1296 || (token_owner && is_token_owner()?))1297 {1298 fail!(<Error<T>>::NoPermission);1299 }1300 }1301 }13021303 match value {1304 Some(value) => {1305 stored_properties1306 .try_set(key.clone(), value)1307 .map_err(<Error<T>>::from)?;13081309 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1310 }1311 None => {1312 stored_properties.remove(&key).map_err(<Error<T>>::from)?;13131314 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1315 }1316 }13171318 <PalletEvm<T>>::deposit_log(log.clone());1319 }13201321 set_token_properties(stored_properties);13221323 Ok(())1324 }13251326 1327 1328 1329 1330 1331 1332 pub fn set_allowance_for_all(1333 collection: &CollectionHandle<T>,1334 owner: &T::CrossAccountId,1335 operator: &T::CrossAccountId,1336 approve: bool,1337 set_allowance: impl FnOnce(),1338 log: evm_coder::ethereum::Log,1339 ) -> DispatchResult {1340 if collection.permissions.access() == AccessMode::AllowList {1341 collection.check_allowlist(owner)?;1342 collection.check_allowlist(operator)?;1343 }13441345 Self::ensure_correct_receiver(operator)?;13461347 set_allowance();13481349 <PalletEvm<T>>::deposit_log(log);1350 Self::deposit_event(Event::ApprovedForAll(1351 collection.id,1352 owner.clone(),1353 operator.clone(),1354 approve,1355 ));1356 Ok(())1357 }13581359 1360 1361 1362 1363 1364 pub fn set_collection_property(1365 collection: &CollectionHandle<T>,1366 sender: &T::CrossAccountId,1367 property: Property,1368 ) -> DispatchResult {1369 Self::set_collection_properties(collection, sender, [property].into_iter())1370 }13711372 1373 1374 1375 1376 1377 1378 pub fn set_scoped_collection_property(1379 collection_id: CollectionId,1380 scope: PropertyScope,1381 property: Property,1382 ) -> DispatchResult {1383 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1384 properties.try_scoped_set(scope, property.key, property.value)1385 })1386 .map_err(<Error<T>>::from)?;13871388 Ok(())1389 }13901391 1392 1393 1394 1395 1396 1397 pub fn set_scoped_collection_properties(1398 collection_id: CollectionId,1399 scope: PropertyScope,1400 properties: impl Iterator<Item = Property>,1401 ) -> DispatchResult {1402 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1403 stored_properties.try_scoped_set_from_iter(scope, properties)1404 })1405 .map_err(<Error<T>>::from)?;14061407 Ok(())1408 }14091410 1411 1412 1413 1414 1415 pub fn set_collection_properties(1416 collection: &CollectionHandle<T>,1417 sender: &T::CrossAccountId,1418 properties: impl Iterator<Item = Property>,1419 ) -> DispatchResult {1420 Self::modify_collection_properties(1421 collection,1422 sender,1423 properties.map(|property| (property.key, Some(property.value))),1424 )1425 }14261427 1428 1429 1430 1431 1432 pub fn delete_collection_property(1433 collection: &CollectionHandle<T>,1434 sender: &T::CrossAccountId,1435 property_key: PropertyKey,1436 ) -> DispatchResult {1437 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1438 }14391440 1441 1442 1443 1444 1445 pub fn delete_collection_properties(1446 collection: &CollectionHandle<T>,1447 sender: &T::CrossAccountId,1448 property_keys: impl Iterator<Item = PropertyKey>,1449 ) -> DispatchResult {1450 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1451 }14521453 1454 1455 1456 1457 1458 1459 pub fn set_property_permission_unchecked(1460 collection: CollectionId,1461 property_permission: PropertyKeyPermission,1462 ) -> DispatchResult {1463 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1464 permissions.try_set(property_permission.key, property_permission.permission)1465 })1466 .map_err(<Error<T>>::from)?;1467 Ok(())1468 }14691470 1471 1472 1473 1474 1475 pub fn set_property_permission(1476 collection: &CollectionHandle<T>,1477 sender: &T::CrossAccountId,1478 property_permission: PropertyKeyPermission,1479 ) -> DispatchResult {1480 Self::set_scoped_property_permission(1481 collection,1482 sender,1483 PropertyScope::None,1484 property_permission,1485 )1486 }14871488 1489 1490 1491 1492 1493 1494 pub fn set_scoped_property_permission(1495 collection: &CollectionHandle<T>,1496 sender: &T::CrossAccountId,1497 scope: PropertyScope,1498 property_permission: PropertyKeyPermission,1499 ) -> DispatchResult {1500 collection.check_is_owner_or_admin(sender)?;15011502 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1503 let current_permission = all_permissions.get(&property_permission.key);1504 if matches![1505 current_permission,1506 Some(PropertyPermission { mutable: false, .. })1507 ] {1508 return Err(<Error<T>>::NoPermission.into());1509 }15101511 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1512 let property_permission = property_permission.clone();1513 permissions.try_scoped_set(1514 scope,1515 property_permission.key,1516 property_permission.permission,1517 )1518 })1519 .map_err(<Error<T>>::from)?;15201521 Self::deposit_event(Event::PropertyPermissionSet(1522 collection.id,1523 property_permission.key,1524 ));1525 <PalletEvm<T>>::deposit_log(1526 erc::CollectionHelpersEvents::CollectionChanged {1527 collection_id: eth::collection_id_to_address(collection.id),1528 }1529 .to_log(T::ContractAddress::get()),1530 );15311532 Ok(())1533 }15341535 1536 1537 1538 1539 1540 #[transactional]1541 pub fn set_token_property_permissions(1542 collection: &CollectionHandle<T>,1543 sender: &T::CrossAccountId,1544 property_permissions: Vec<PropertyKeyPermission>,1545 ) -> DispatchResult {1546 Self::set_scoped_token_property_permissions(1547 collection,1548 sender,1549 PropertyScope::None,1550 property_permissions,1551 )1552 }15531554 1555 1556 1557 1558 1559 1560 #[transactional]1561 pub fn set_scoped_token_property_permissions(1562 collection: &CollectionHandle<T>,1563 sender: &T::CrossAccountId,1564 scope: PropertyScope,1565 property_permissions: Vec<PropertyKeyPermission>,1566 ) -> DispatchResult {1567 for prop_pemission in property_permissions {1568 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1569 }15701571 Ok(())1572 }15731574 1575 pub fn get_collection_property(1576 collection_id: CollectionId,1577 key: &PropertyKey,1578 ) -> Option<PropertyValue> {1579 Self::collection_properties(collection_id).get(key).cloned()1580 }15811582 1583 pub fn bytes_keys_to_property_keys(1584 keys: Vec<Vec<u8>>,1585 ) -> Result<Vec<PropertyKey>, DispatchError> {1586 keys.into_iter()1587 .map(|key| -> Result<PropertyKey, DispatchError> {1588 key.try_into()1589 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1590 })1591 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1592 }15931594 1595 pub fn filter_collection_properties(1596 collection_id: CollectionId,1597 keys: Option<Vec<PropertyKey>>,1598 ) -> Result<Vec<Property>, DispatchError> {1599 let properties = Self::collection_properties(collection_id);16001601 let properties = keys1602 .map(|keys| {1603 keys.into_iter()1604 .filter_map(|key| {1605 properties.get(&key).map(|value| Property {1606 key,1607 value: value.clone(),1608 })1609 })1610 .collect()1611 })1612 .unwrap_or_else(|| {1613 properties1614 .into_iter()1615 .map(|(key, value)| Property { key, value })1616 .collect()1617 });16181619 Ok(properties)1620 }16211622 1623 pub fn filter_property_permissions(1624 collection_id: CollectionId,1625 keys: Option<Vec<PropertyKey>>,1626 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1627 let permissions = Self::property_permissions(collection_id);16281629 let key_permissions = keys1630 .map(|keys| {1631 keys.into_iter()1632 .filter_map(|key| {1633 permissions1634 .get(&key)1635 .map(|permission| PropertyKeyPermission {1636 key,1637 permission: permission.clone(),1638 })1639 })1640 .collect()1641 })1642 .unwrap_or_else(|| {1643 permissions1644 .into_iter()1645 .map(|(key, permission)| PropertyKeyPermission { key, permission })1646 .collect()1647 });16481649 Ok(key_permissions)1650 }16511652 1653 1654 1655 pub fn toggle_allowlist(1656 collection: &CollectionHandle<T>,1657 sender: &T::CrossAccountId,1658 user: &T::CrossAccountId,1659 allowed: bool,1660 ) -> DispatchResult {1661 collection.check_is_owner_or_admin(sender)?;16621663 16641665 if allowed {1666 <Allowlist<T>>::insert((collection.id, user), true);1667 Self::deposit_event(Event::<T>::AllowListAddressAdded(1668 collection.id,1669 user.clone(),1670 ));1671 } else {1672 <Allowlist<T>>::remove((collection.id, user));1673 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1674 collection.id,1675 user.clone(),1676 ));1677 }16781679 <PalletEvm<T>>::deposit_log(1680 erc::CollectionHelpersEvents::CollectionChanged {1681 collection_id: eth::collection_id_to_address(collection.id),1682 }1683 .to_log(T::ContractAddress::get()),1684 );16851686 Ok(())1687 }16881689 1690 1691 1692 pub fn toggle_admin(1693 collection: &CollectionHandle<T>,1694 sender: &T::CrossAccountId,1695 user: &T::CrossAccountId,1696 admin: bool,1697 ) -> DispatchResult {1698 collection.check_is_internal()?;1699 collection.check_is_owner(sender)?;17001701 let is_admin = <IsAdmin<T>>::get((collection.id, user));1702 if is_admin == admin {1703 if admin {1704 return Ok(());1705 } else {1706 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1707 }1708 }1709 let amount = <AdminAmount<T>>::get(collection.id);17101711 17121713 if admin {1714 let amount = amount1715 .checked_add(1)1716 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1717 ensure!(1718 amount <= Self::collection_admins_limit(),1719 <Error<T>>::CollectionAdminCountExceeded,1720 );17211722 <AdminAmount<T>>::insert(collection.id, amount);1723 <IsAdmin<T>>::insert((collection.id, user), true);17241725 Self::deposit_event(Event::<T>::CollectionAdminAdded(1726 collection.id,1727 user.clone(),1728 ));1729 } else {1730 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1731 <IsAdmin<T>>::remove((collection.id, user));17321733 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1734 collection.id,1735 user.clone(),1736 ));1737 }17381739 <PalletEvm<T>>::deposit_log(1740 erc::CollectionHelpersEvents::CollectionChanged {1741 collection_id: eth::collection_id_to_address(collection.id),1742 }1743 .to_log(T::ContractAddress::get()),1744 );17451746 Ok(())1747 }17481749 1750 pub fn update_limits(1751 user: &T::CrossAccountId,1752 collection: &mut CollectionHandle<T>,1753 new_limit: CollectionLimits,1754 ) -> DispatchResult {1755 collection.check_is_internal()?;1756 collection.check_is_owner_or_admin(user)?;17571758 collection.limits =1759 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;17601761 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1762 <PalletEvm<T>>::deposit_log(1763 erc::CollectionHelpersEvents::CollectionChanged {1764 collection_id: eth::collection_id_to_address(collection.id),1765 }1766 .to_log(T::ContractAddress::get()),1767 );17681769 collection.save()1770 }17711772 1773 fn clamp_limits(1774 mode: CollectionMode,1775 old_limit: &CollectionLimits,1776 mut new_limit: CollectionLimits,1777 ) -> Result<CollectionLimits, DispatchError> {1778 let limits = old_limit;1779 limit_default!(old_limit, new_limit,1780 account_token_ownership_limit => ensure!(1781 new_limit <= MAX_TOKEN_OWNERSHIP,1782 <Error<T>>::CollectionLimitBoundsExceeded,1783 ),1784 sponsored_data_size => ensure!(1785 new_limit <= CUSTOM_DATA_LIMIT,1786 <Error<T>>::CollectionLimitBoundsExceeded,1787 ),17881789 sponsored_data_rate_limit => {},1790 token_limit => ensure!(1791 old_limit >= new_limit && new_limit > 0,1792 <Error<T>>::CollectionTokenLimitExceeded1793 ),17941795 sponsor_transfer_timeout(match mode {1796 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1797 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1798 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1799 }) => ensure!(1800 new_limit <= MAX_SPONSOR_TIMEOUT,1801 <Error<T>>::CollectionLimitBoundsExceeded,1802 ),1803 sponsor_approve_timeout => {},1804 owner_can_transfer => ensure!(1805 !limits.owner_can_transfer_instaled() ||1806 old_limit || !new_limit,1807 <Error<T>>::OwnerPermissionsCantBeReverted,1808 ),1809 owner_can_destroy => ensure!(1810 old_limit || !new_limit,1811 <Error<T>>::OwnerPermissionsCantBeReverted,1812 ),1813 transfers_enabled => {},1814 );1815 Ok(new_limit)1816 }18171818 1819 pub fn update_permissions(1820 user: &T::CrossAccountId,1821 collection: &mut CollectionHandle<T>,1822 new_permission: CollectionPermissions,1823 ) -> DispatchResult {1824 collection.check_is_internal()?;1825 collection.check_is_owner_or_admin(user)?;1826 collection.permissions = Self::clamp_permissions(1827 collection.mode.clone(),1828 &collection.permissions,1829 new_permission,1830 )?;18311832 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1833 <PalletEvm<T>>::deposit_log(1834 erc::CollectionHelpersEvents::CollectionChanged {1835 collection_id: eth::collection_id_to_address(collection.id),1836 }1837 .to_log(T::ContractAddress::get()),1838 );18391840 collection.save()1841 }18421843 1844 fn clamp_permissions(1845 _mode: CollectionMode,1846 old_permission: &CollectionPermissions,1847 mut new_permission: CollectionPermissions,1848 ) -> Result<CollectionPermissions, DispatchError> {1849 limit_default_clone!(old_permission, new_permission,1850 access => {},1851 mint_mode => {},1852 nesting => { },1853 );1854 Ok(new_permission)1855 }18561857 1858 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1859 CollectionProperties::<T>::mutate(collection_id, |properties| {1860 properties.recompute_consumed_space();1861 });18621863 Ok(())1864 }1865}186618671868#[macro_export]1869macro_rules! unsupported {1870 ($runtime:path) => {1871 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1872 };1873}187418751876pub trait CommonWeightInfo<CrossAccountId> {1877 1878 fn create_item(data: &CreateItemData) -> Weight {1879 Self::create_multiple_items(from_ref(data))1880 }18811882 1883 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;18841885 1886 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;18871888 1889 fn burn_item() -> Weight;18901891 1892 1893 1894 fn set_collection_properties(amount: u32) -> Weight;18951896 1897 1898 1899 fn delete_collection_properties(amount: u32) -> Weight;19001901 1902 1903 1904 fn set_token_properties(amount: u32) -> Weight;19051906 1907 1908 1909 fn delete_token_properties(amount: u32) -> Weight;19101911 1912 1913 1914 fn set_token_property_permissions(amount: u32) -> Weight;19151916 1917 fn transfer() -> Weight;19181919 1920 fn approve() -> Weight;19211922 1923 fn approve_from() -> Weight;19241925 1926 fn transfer_from() -> Weight;19271928 1929 fn burn_from() -> Weight;19301931 1932 1933 1934 1935 fn burn_recursively_self_raw() -> Weight;19361937 1938 1939 1940 fn burn_recursively_breadth_raw(amount: u32) -> Weight;19411942 1943 1944 1945 1946 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1947 Self::burn_recursively_self_raw()1948 .saturating_mul(max_selfs.max(1) as u64)1949 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1950 }19511952 1953 fn token_owner() -> Weight;19541955 1956 fn set_allowance_for_all() -> Weight;19571958 1959 fn force_repair_item() -> Weight;1960}196119621963pub trait RefungibleExtensionsWeightInfo {1964 1965 fn repartition() -> Weight;1966}196719681969197019711972pub trait CommonCollectionOperations<T: Config> {1973 1974 1975 1976 1977 1978 1979 fn create_item(1980 &self,1981 sender: T::CrossAccountId,1982 to: T::CrossAccountId,1983 data: CreateItemData,1984 nesting_budget: &dyn Budget,1985 ) -> DispatchResultWithPostInfo;19861987 1988 1989 1990 1991 1992 1993 fn create_multiple_items(1994 &self,1995 sender: T::CrossAccountId,1996 to: T::CrossAccountId,1997 data: Vec<CreateItemData>,1998 nesting_budget: &dyn Budget,1999 ) -> DispatchResultWithPostInfo;20002001 2002 2003 2004 2005 2006 2007 fn create_multiple_items_ex(2008 &self,2009 sender: T::CrossAccountId,2010 data: CreateItemExData<T::CrossAccountId>,2011 nesting_budget: &dyn Budget,2012 ) -> DispatchResultWithPostInfo;20132014 2015 2016 2017 2018 2019 fn burn_item(2020 &self,2021 sender: T::CrossAccountId,2022 token: TokenId,2023 amount: u128,2024 ) -> DispatchResultWithPostInfo;20252026 2027 2028 2029 2030 2031 2032 fn burn_item_recursively(2033 &self,2034 sender: T::CrossAccountId,2035 token: TokenId,2036 self_budget: &dyn Budget,2037 breadth_budget: &dyn Budget,2038 ) -> DispatchResultWithPostInfo;20392040 2041 2042 2043 2044 fn set_collection_properties(2045 &self,2046 sender: T::CrossAccountId,2047 properties: Vec<Property>,2048 ) -> DispatchResultWithPostInfo;20492050 2051 2052 2053 2054 fn delete_collection_properties(2055 &self,2056 sender: &T::CrossAccountId,2057 property_keys: Vec<PropertyKey>,2058 ) -> DispatchResultWithPostInfo;20592060 2061 2062 2063 2064 2065 2066 2067 2068 2069 fn set_token_properties(2070 &self,2071 sender: T::CrossAccountId,2072 token_id: TokenId,2073 properties: Vec<Property>,2074 budget: &dyn Budget,2075 ) -> DispatchResultWithPostInfo;20762077 2078 2079 2080 2081 2082 2083 2084 2085 2086 fn delete_token_properties(2087 &self,2088 sender: T::CrossAccountId,2089 token_id: TokenId,2090 property_keys: Vec<PropertyKey>,2091 budget: &dyn Budget,2092 ) -> DispatchResultWithPostInfo;20932094 2095 2096 2097 2098 2099 2100 fn set_token_property_permissions(2101 &self,2102 sender: &T::CrossAccountId,2103 property_permissions: Vec<PropertyKeyPermission>,2104 ) -> DispatchResultWithPostInfo;21052106 2107 2108 2109 2110 2111 2112 2113 fn transfer(2114 &self,2115 sender: T::CrossAccountId,2116 to: T::CrossAccountId,2117 token: TokenId,2118 amount: u128,2119 budget: &dyn Budget,2120 ) -> DispatchResultWithPostInfo;21212122 2123 2124 2125 2126 2127 2128 fn approve(2129 &self,2130 sender: T::CrossAccountId,2131 spender: T::CrossAccountId,2132 token: TokenId,2133 amount: u128,2134 ) -> DispatchResultWithPostInfo;21352136 2137 2138 2139 2140 2141 2142 2143 fn approve_from(2144 &self,2145 sender: T::CrossAccountId,2146 from: T::CrossAccountId,2147 to: T::CrossAccountId,2148 token: TokenId,2149 amount: u128,2150 ) -> DispatchResultWithPostInfo;21512152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 fn transfer_from(2163 &self,2164 sender: T::CrossAccountId,2165 from: T::CrossAccountId,2166 to: T::CrossAccountId,2167 token: TokenId,2168 amount: u128,2169 budget: &dyn Budget,2170 ) -> DispatchResultWithPostInfo;21712172 2173 2174 2175 2176 2177 2178 2179 2180 2181 fn burn_from(2182 &self,2183 sender: T::CrossAccountId,2184 from: T::CrossAccountId,2185 token: TokenId,2186 amount: u128,2187 budget: &dyn Budget,2188 ) -> DispatchResultWithPostInfo;21892190 2191 2192 2193 2194 2195 2196 fn check_nesting(2197 &self,2198 sender: T::CrossAccountId,2199 from: (CollectionId, TokenId),2200 under: TokenId,2201 budget: &dyn Budget,2202 ) -> DispatchResult;22032204 2205 2206 2207 2208 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22092210 2211 2212 2213 2214 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22152216 2217 2218 2219 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;22202221 2222 fn collection_tokens(&self) -> Vec<TokenId>;22232224 2225 2226 2227 fn token_exists(&self, token: TokenId) -> bool;22282229 2230 fn last_token_id(&self) -> TokenId;22312232 2233 2234 2235 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22362237 2238 2239 2240 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22412242 2243 2244 2245 2246 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22472248 2249 2250 2251 2252 2253 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22542255 2256 fn total_supply(&self) -> u32;22572258 2259 2260 2261 fn account_balance(&self, account: T::CrossAccountId) -> u32;22622263 2264 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;22652266 2267 fn total_pieces(&self, token: TokenId) -> Option<u128>;22682269 2270 2271 2272 2273 2274 fn allowance(2275 &self,2276 sender: T::CrossAccountId,2277 spender: T::CrossAccountId,2278 token: TokenId,2279 ) -> u128;22802281 2282 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;22832284 2285 2286 2287 2288 fn set_allowance_for_all(2289 &self,2290 owner: T::CrossAccountId,2291 operator: T::CrossAccountId,2292 approve: bool,2293 ) -> DispatchResultWithPostInfo;22942295 2296 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;22972298 2299 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2300}230123022303pub trait RefungibleExtensions<T>2304where2305 T: Config,2306{2307 2308 2309 2310 2311 2312 2313 2314 fn repartition(2315 &self,2316 sender: &T::CrossAccountId,2317 token: TokenId,2318 amount: u128,2319 ) -> DispatchResultWithPostInfo;2320}23212322232323242325pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2326 let post_info = PostDispatchInfo {2327 actual_weight: Some(weight),2328 pays_fee: Pays::Yes,2329 };2330 match res {2331 Ok(()) => Ok(post_info),2332 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2333 }2334}23352336impl<T: Config> From<PropertiesError> for Error<T> {2337 fn from(error: PropertiesError) -> Self {2338 match error {2339 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2340 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2341 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2342 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2343 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2344 }2345 }2346}