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;95#[allow(missing_docs)]96pub mod weights;979899pub 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 <CollectionById<T>>::get(id).map(|collection| Self {129 id,130 collection,131 recorder: SubstrateRecorder::new(gas_limit),132 })133 }134135 136 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {137 <CollectionById<T>>::get(id).map(|collection| Self {138 id,139 collection,140 recorder,141 })142 }143144 145 146 pub fn new(id: CollectionId) -> Option<Self> {147 Self::new_with_gas_limit(id, u64::MAX)148 }149150 151 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {152 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)153 }154155 156 pub fn consume_store_reads(157 &self,158 reads: u64,159 ) -> pallet_evm_coder_substrate::execution::Result<()> {160 self.recorder161 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(162 <T as frame_system::Config>::DbWeight::get()163 .read164 .saturating_mul(reads),165 166 0,167 )))168 }169170 171 pub fn consume_store_writes(172 &self,173 writes: u64,174 ) -> pallet_evm_coder_substrate::execution::Result<()> {175 self.recorder176 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(177 <T as frame_system::Config>::DbWeight::get()178 .write179 .saturating_mul(writes),180 181 0,182 )))183 }184185 186 pub fn consume_store_reads_and_writes(187 &self,188 reads: u64,189 writes: u64,190 ) -> pallet_evm_coder_substrate::execution::Result<()> {191 let weight = <T as frame_system::Config>::DbWeight::get();192 let reads = weight.read.saturating_mul(reads);193 let writes = weight.read.saturating_mul(writes);194 self.recorder195 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(196 reads.saturating_add(writes),197 198 0,199 )))200 }201202 203 pub fn save(&self) -> DispatchResult {204 <CollectionById<T>>::insert(self.id, &self.collection);205 Ok(())206 }207208 209 210 211 212 213 pub fn set_sponsor(214 &mut self,215 sender: &T::CrossAccountId,216 sponsor: T::AccountId,217 ) -> DispatchResult {218 self.check_is_internal()?;219 self.check_is_owner_or_admin(sender)?;220221 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());222223 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));224 <PalletEvm<T>>::deposit_log(225 erc::CollectionHelpersEvents::CollectionChanged {226 collection_id: eth::collection_id_to_address(self.id),227 }228 .to_log(T::ContractAddress::get()),229 );230231 self.save()232 }233234 235 236 237 238 239 240 241 242 243 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {244 self.check_is_internal()?;245246 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());247248 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));249 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));250 <PalletEvm<T>>::deposit_log(251 erc::CollectionHelpersEvents::CollectionChanged {252 collection_id: eth::collection_id_to_address(self.id),253 }254 .to_log(T::ContractAddress::get()),255 );256257 self.save()258 }259260 261 262 263 264 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {265 self.check_is_internal()?;266 ensure!(267 self.collection.sponsorship.pending_sponsor() == Some(sender),268 Error::<T>::ConfirmSponsorshipFail269 );270271 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());272273 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));274 <PalletEvm<T>>::deposit_log(275 erc::CollectionHelpersEvents::CollectionChanged {276 collection_id: eth::collection_id_to_address(self.id),277 }278 .to_log(T::ContractAddress::get()),279 );280281 self.save()282 }283284 285 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {286 self.check_is_internal()?;287 self.check_is_owner_or_admin(sender)?;288289 self.collection.sponsorship = SponsorshipState::Disabled;290291 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));292 <PalletEvm<T>>::deposit_log(293 erc::CollectionHelpersEvents::CollectionChanged {294 collection_id: eth::collection_id_to_address(self.id),295 }296 .to_log(T::ContractAddress::get()),297 );298 self.save()299 }300301 302 303 304 305 pub fn force_remove_sponsor(&mut self) -> DispatchResult {306 self.check_is_internal()?;307308 self.collection.sponsorship = SponsorshipState::Disabled;309310 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));311 <PalletEvm<T>>::deposit_log(312 erc::CollectionHelpersEvents::CollectionChanged {313 collection_id: eth::collection_id_to_address(self.id),314 }315 .to_log(T::ContractAddress::get()),316 );317 self.save()318 }319320 321 322 pub fn check_is_internal(&self) -> DispatchResult {323 if self.flags.external {324 return Err(<Error<T>>::CollectionIsExternal)?;325 }326327 Ok(())328 }329330 331 332 pub fn check_is_external(&self) -> DispatchResult {333 if !self.flags.external {334 return Err(<Error<T>>::CollectionIsInternal)?;335 }336337 Ok(())338 }339}340341impl<T: Config> Deref for CollectionHandle<T> {342 type Target = Collection<T::AccountId>;343344 fn deref(&self) -> &Self::Target {345 &self.collection346 }347}348349impl<T: Config> DerefMut for CollectionHandle<T> {350 fn deref_mut(&mut self) -> &mut Self::Target {351 &mut self.collection352 }353}354355impl<T: Config> CollectionHandle<T> {356 357 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {358 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);359 Ok(())360 }361362 363 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {364 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))365 }366367 368 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {369 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);370 Ok(())371 }372373 374 375 376 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {377 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)378 }379380 381 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {382 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)383 }384385 386 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {387 ensure!(388 <Allowlist<T>>::get((self.id, user)),389 <Error<T>>::AddressNotInAllowlist390 );391 Ok(())392 }393394 395 396 397 pub fn change_owner(398 &mut self,399 caller: T::CrossAccountId,400 new_owner: T::CrossAccountId,401 ) -> DispatchResult {402 self.check_is_internal()?;403 self.check_is_owner(&caller)?;404 self.collection.owner = new_owner.as_sub().clone();405406 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(407 self.id,408 new_owner.as_sub().clone(),409 ));410 <PalletEvm<T>>::deposit_log(411 erc::CollectionHelpersEvents::CollectionChanged {412 collection_id: eth::collection_id_to_address(self.id),413 }414 .to_log(T::ContractAddress::get()),415 );416417 self.save()418 }419}420421#[frame_support::pallet]422pub mod pallet {423 use super::*;424 use dispatch::CollectionDispatch;425 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};426 use frame_system::pallet_prelude::*;427 use frame_support::traits::Currency;428 use up_data_structs::{TokenId, mapping::TokenAddressMapping};429 use scale_info::TypeInfo;430 use weights::WeightInfo;431432 #[pallet::config]433 pub trait Config:434 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo435 {436 437 type WeightInfo: WeightInfo;438439 440 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;441442 443 type Currency: Currency<Self::AccountId>;444445 446 #[pallet::constant]447 type CollectionCreationPrice: Get<448 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,449 >;450451 452 type CollectionDispatch: CollectionDispatch<Self>;453454 455 type TreasuryAccountId: Get<Self::AccountId>;456457 458 #[pallet::constant]459 type ContractAddress: Get<H160>;460461 462 type EvmTokenAddressMapping: TokenAddressMapping<H160>;463464 465 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;466 }467468 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);469470 #[pallet::pallet]471 #[pallet::storage_version(STORAGE_VERSION)]472 pub struct Pallet<T>(_);473474 #[pallet::extra_constants]475 impl<T: Config> Pallet<T> {476 477 pub fn collection_admins_limit() -> u32 {478 COLLECTION_ADMINS_LIMIT479 }480 }481482 impl<T: Config> Pallet<T> {483 484 pub fn deposit_event(event: Event<T>) {485 let event = <T as Config>::RuntimeEvent::from(event);486 let event = event.into();487 <frame_system::Pallet<T>>::deposit_event(event)488 }489 }490491 #[pallet::event]492 pub enum Event<T: Config> {493 494 CollectionCreated(495 496 CollectionId,497 498 u8,499 500 T::AccountId,501 ),502503 504 CollectionDestroyed(505 506 CollectionId,507 ),508509 510 ItemCreated(511 512 CollectionId,513 514 TokenId,515 516 T::CrossAccountId,517 518 u128,519 ),520521 522 ItemDestroyed(523 524 CollectionId,525 526 TokenId,527 528 T::CrossAccountId,529 530 u128,531 ),532533 534 Transfer(535 536 CollectionId,537 538 TokenId,539 540 T::CrossAccountId,541 542 T::CrossAccountId,543 544 u128,545 ),546547 548 Approved(549 550 CollectionId,551 552 TokenId,553 554 T::CrossAccountId,555 556 T::CrossAccountId,557 558 u128,559 ),560561 562 ApprovedForAll(563 564 CollectionId,565 566 T::CrossAccountId,567 568 T::CrossAccountId,569 570 bool,571 ),572573 574 CollectionPropertySet(575 576 CollectionId,577 578 PropertyKey,579 ),580581 582 CollectionPropertyDeleted(583 584 CollectionId,585 586 PropertyKey,587 ),588589 590 TokenPropertySet(591 592 CollectionId,593 594 TokenId,595 596 PropertyKey,597 ),598599 600 TokenPropertyDeleted(601 602 CollectionId,603 604 TokenId,605 606 PropertyKey,607 ),608609 610 PropertyPermissionSet(611 612 CollectionId,613 614 PropertyKey,615 ),616617 618 AllowListAddressAdded(619 620 CollectionId,621 622 T::CrossAccountId,623 ),624625 626 AllowListAddressRemoved(627 628 CollectionId,629 630 T::CrossAccountId,631 ),632633 634 CollectionAdminAdded(635 636 CollectionId,637 638 T::CrossAccountId,639 ),640641 642 CollectionAdminRemoved(643 644 CollectionId,645 646 T::CrossAccountId,647 ),648649 650 CollectionLimitSet(651 652 CollectionId,653 ),654655 656 CollectionOwnerChanged(657 658 CollectionId,659 660 T::AccountId,661 ),662663 664 CollectionPermissionSet(665 666 CollectionId,667 ),668669 670 CollectionSponsorSet(671 672 CollectionId,673 674 T::AccountId,675 ),676677 678 SponsorshipConfirmed(679 680 CollectionId,681 682 T::AccountId,683 ),684685 686 CollectionSponsorRemoved(687 688 CollectionId,689 ),690 }691692 #[pallet::error]693 pub enum Error<T> {694 695 CollectionNotFound,696 697 MustBeTokenOwner,698 699 NoPermission,700 701 CantDestroyNotEmptyCollection,702 703 PublicMintingNotAllowed,704 705 AddressNotInAllowlist,706707 708 CollectionNameLimitExceeded,709 710 CollectionDescriptionLimitExceeded,711 712 CollectionTokenPrefixLimitExceeded,713 714 TotalCollectionsLimitExceeded,715 716 CollectionAdminCountExceeded,717 718 CollectionLimitBoundsExceeded,719 720 OwnerPermissionsCantBeReverted,721 722 TransferNotAllowed,723 724 AccountTokenLimitExceeded,725 726 CollectionTokenLimitExceeded,727 728 MetadataFlagFrozen,729730 731 TokenNotFound,732 733 TokenValueTooLow,734 735 ApprovedValueTooLow,736 737 CantApproveMoreThanOwned,738 739 AddressIsNotEthMirror,740741 742 AddressIsZero,743744 745 UnsupportedOperation,746747 748 NotSufficientFounds,749750 751 UserIsNotAllowedToNest,752 753 SourceCollectionIsNotAllowedToNest,754755 756 CollectionFieldSizeExceeded,757758 759 NoSpaceForProperty,760761 762 PropertyLimitReached,763764 765 PropertyKeyIsTooLong,766767 768 InvalidCharacterInPropertyKey,769770 771 EmptyPropertyKey,772773 774 CollectionIsExternal,775776 777 CollectionIsInternal,778779 780 ConfirmSponsorshipFail,781782 783 UserIsNotCollectionAdmin,784 }785786 787 #[pallet::storage]788 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;789790 791 #[pallet::storage]792 pub type DestroyedCollectionCount<T> =793 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;794795 796 #[pallet::storage]797 pub type CollectionById<T> = StorageMap<798 Hasher = Blake2_128Concat,799 Key = CollectionId,800 Value = Collection<<T as frame_system::Config>::AccountId>,801 QueryKind = OptionQuery,802 >;803804 805 #[pallet::storage]806 #[pallet::getter(fn collection_properties)]807 pub type CollectionProperties<T> = StorageMap<808 Hasher = Blake2_128Concat,809 Key = CollectionId,810 Value = CollectionPropertiesT,811 QueryKind = ValueQuery,812 >;813814 815 #[pallet::storage]816 #[pallet::getter(fn property_permissions)]817 pub type CollectionPropertyPermissions<T> = StorageMap<818 Hasher = Blake2_128Concat,819 Key = CollectionId,820 Value = PropertiesPermissionMap,821 QueryKind = ValueQuery,822 >;823824 825 #[pallet::storage]826 pub type AdminAmount<T> = StorageMap<827 Hasher = Blake2_128Concat,828 Key = CollectionId,829 Value = u32,830 QueryKind = ValueQuery,831 >;832833 834 #[pallet::storage]835 pub type IsAdmin<T: Config> = StorageNMap<836 Key = (837 Key<Blake2_128Concat, CollectionId>,838 Key<Blake2_128Concat, T::CrossAccountId>,839 ),840 Value = bool,841 QueryKind = ValueQuery,842 >;843844 845 #[pallet::storage]846 pub type Allowlist<T: Config> = StorageNMap<847 Key = (848 Key<Blake2_128Concat, CollectionId>,849 Key<Blake2_128Concat, T::CrossAccountId>,850 ),851 Value = bool,852 QueryKind = ValueQuery,853 >;854855 856 #[pallet::storage]857 pub type DummyStorageValue<T: Config> = StorageValue<858 Value = (859 CollectionStats,860 CollectionId,861 TokenId,862 TokenChild,863 PhantomType<(864 TokenData<T::CrossAccountId>,865 RpcCollection<T::AccountId>,866 867 PovInfo,868 )>,869 ),870 QueryKind = OptionQuery,871 >;872}873874impl<T: Config> Pallet<T> {875 876 877 878 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {879 ensure!(880 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,881 <Error<T>>::AddressIsZero882 );883 Ok(())884 }885886 887 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {888 <IsAdmin<T>>::iter_prefix((collection,))889 .map(|(a, _)| a)890 .collect()891 }892893 894 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {895 <Allowlist<T>>::iter_prefix((collection,))896 .map(|(a, _)| a)897 .collect()898 }899900 901 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {902 <Allowlist<T>>::get((collection, user))903 }904905 906 pub fn collection_stats() -> CollectionStats {907 let created = <CreatedCollectionCount<T>>::get();908 let destroyed = <DestroyedCollectionCount<T>>::get();909 CollectionStats {910 created: created.0,911 destroyed: destroyed.0,912 alive: created.0 - destroyed.0,913 }914 }915916 917 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {918 let collection = <CollectionById<T>>::get(collection)?;919 let limits = collection.limits;920 let effective_limits = CollectionLimits {921 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),922 sponsored_data_size: Some(limits.sponsored_data_size()),923 sponsored_data_rate_limit: Some(924 limits925 .sponsored_data_rate_limit926 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),927 ),928 token_limit: Some(limits.token_limit()),929 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(930 match collection.mode {931 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,932 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,933 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,934 },935 )),936 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),937 owner_can_transfer: Some(limits.owner_can_transfer()),938 owner_can_destroy: Some(limits.owner_can_destroy()),939 transfers_enabled: Some(limits.transfers_enabled()),940 };941942 Some(effective_limits)943 }944945 946 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {947 let Collection {948 name,949 description,950 owner,951 mode,952 token_prefix,953 sponsorship,954 limits,955 permissions,956 flags,957 } = <CollectionById<T>>::get(collection)?;958959 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)960 .into_iter()961 .map(|(key, permission)| PropertyKeyPermission { key, permission })962 .collect();963964 let properties = <CollectionProperties<T>>::get(collection)965 .into_iter()966 .map(|(key, value)| Property { key, value })967 .collect();968969 let permissions = CollectionPermissions {970 access: Some(permissions.access()),971 mint_mode: Some(permissions.mint_mode()),972 nesting: Some(permissions.nesting().clone()),973 };974975 Some(RpcCollection {976 name: name.into_inner(),977 description: description.into_inner(),978 owner,979 mode,980 token_prefix: token_prefix.into_inner(),981 sponsorship,982 limits,983 permissions,984 token_property_permissions,985 properties,986 read_only: flags.external,987988 flags: RpcCollectionFlags {989 foreign: flags.foreign,990 erc721metadata: flags.erc721metadata,991 },992 })993 }994}995996macro_rules! limit_default {997 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{998 $(999 if let Some($new) = $new.$field {1000 let $old = $old.$field($($arg)?);1001 let _ = $new;1002 let _ = $old;1003 $check1004 } else {1005 $new.$field = $old.$field1006 }1007 )*1008 }};1009}1010macro_rules! limit_default_clone {1011 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1012 $(1013 if let Some($new) = $new.$field.clone() {1014 let $old = $old.$field($($arg)?);1015 let _ = $new;1016 let _ = $old;1017 $check1018 } else {1019 $new.$field = $old.$field.clone()1020 }1021 )*1022 }};1023}10241025impl<T: Config> Pallet<T> {1026 1027 1028 1029 1030 1031 pub fn init_collection(1032 owner: T::CrossAccountId,1033 payer: T::CrossAccountId,1034 data: CreateCollectionData<T::AccountId>,1035 flags: CollectionFlags,1036 ) -> Result<CollectionId, DispatchError> {1037 {1038 ensure!(1039 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1040 Error::<T>::CollectionTokenPrefixLimitExceeded1041 );1042 }10431044 let created_count = <CreatedCollectionCount<T>>::get()1045 .01046 .checked_add(1)1047 .ok_or(ArithmeticError::Overflow)?;1048 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1049 let id = CollectionId(created_count);10501051 1052 ensure!(1053 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1054 <Error<T>>::TotalCollectionsLimitExceeded1055 );10561057 10581059 let collection = Collection {1060 owner: owner.as_sub().clone(),1061 name: data.name,1062 mode: data.mode.clone(),1063 description: data.description,1064 token_prefix: data.token_prefix,1065 sponsorship: data1066 .pending_sponsor1067 .map(SponsorshipState::Unconfirmed)1068 .unwrap_or_default(),1069 limits: data1070 .limits1071 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1072 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1073 permissions: data1074 .permissions1075 .map(|permissions| {1076 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1077 })1078 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1079 flags,1080 };10811082 let mut collection_properties = CollectionPropertiesT::new();1083 collection_properties1084 .try_set_from_iter(data.properties.into_iter())1085 .map_err(<Error<T>>::from)?;10861087 CollectionProperties::<T>::insert(id, collection_properties);10881089 let mut token_props_permissions = PropertiesPermissionMap::new();1090 token_props_permissions1091 .try_set_from_iter(data.token_property_permissions.into_iter())1092 .map_err(<Error<T>>::from)?;10931094 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);10951096 1097 {1098 let mut imbalance =1099 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1100 imbalance.subsume(1101 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1102 &T::TreasuryAccountId::get(),1103 T::CollectionCreationPrice::get(),1104 ),1105 );1106 <T as Config>::Currency::settle(1107 payer.as_sub(),1108 imbalance,1109 WithdrawReasons::TRANSFER,1110 ExistenceRequirement::KeepAlive,1111 )1112 .map_err(|_| Error::<T>::NotSufficientFounds)?;1113 }11141115 <CreatedCollectionCount<T>>::put(created_count);1116 <Pallet<T>>::deposit_event(Event::CollectionCreated(1117 id,1118 data.mode.id(),1119 owner.as_sub().clone(),1120 ));1121 <PalletEvm<T>>::deposit_log(1122 erc::CollectionHelpersEvents::CollectionCreated {1123 owner: *owner.as_eth(),1124 collection_id: eth::collection_id_to_address(id),1125 }1126 .to_log(T::ContractAddress::get()),1127 );1128 <CollectionById<T>>::insert(id, collection);1129 Ok(id)1130 }11311132 1133 1134 1135 1136 pub fn destroy_collection(1137 collection: CollectionHandle<T>,1138 sender: &T::CrossAccountId,1139 ) -> DispatchResult {1140 ensure!(1141 collection.limits.owner_can_destroy(),1142 <Error<T>>::NoPermission,1143 );1144 collection.check_is_owner(sender)?;11451146 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1147 .01148 .checked_add(1)1149 .ok_or(ArithmeticError::Overflow)?;11501151 11521153 <DestroyedCollectionCount<T>>::put(destroyed_collections);1154 <CollectionById<T>>::remove(collection.id);1155 <AdminAmount<T>>::remove(collection.id);1156 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1157 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1158 <CollectionProperties<T>>::remove(collection.id);11591160 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11611162 <PalletEvm<T>>::deposit_log(1163 erc::CollectionHelpersEvents::CollectionDestroyed {1164 collection_id: eth::collection_id_to_address(collection.id),1165 }1166 .to_log(T::ContractAddress::get()),1167 );1168 Ok(())1169 }11701171 1172 1173 1174 1175 1176 1177 1178 1179 #[transactional]1180 fn modify_collection_properties(1181 collection: &CollectionHandle<T>,1182 sender: &T::CrossAccountId,1183 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1184 ) -> DispatchResult {1185 collection.check_is_owner_or_admin(sender)?;11861187 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);11881189 for (key, value) in properties_updates {1190 match value {1191 Some(value) => {1192 stored_properties1193 .try_set(key.clone(), value)1194 .map_err(<Error<T>>::from)?;11951196 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1197 <PalletEvm<T>>::deposit_log(1198 erc::CollectionHelpersEvents::CollectionChanged {1199 collection_id: eth::collection_id_to_address(collection.id),1200 }1201 .to_log(T::ContractAddress::get()),1202 );1203 }1204 None => {1205 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12061207 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1208 <PalletEvm<T>>::deposit_log(1209 erc::CollectionHelpersEvents::CollectionChanged {1210 collection_id: eth::collection_id_to_address(collection.id),1211 }1212 .to_log(T::ContractAddress::get()),1213 );1214 }1215 }1216 }12171218 <CollectionProperties<T>>::set(collection.id, stored_properties);12191220 Ok(())1221 }12221223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 pub fn modify_token_properties(1241 collection: &CollectionHandle<T>,1242 sender: &T::CrossAccountId,1243 token_id: TokenId,1244 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1245 is_token_create: bool,1246 mut stored_properties: TokenProperties,1247 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1248 set_token_properties: impl FnOnce(TokenProperties),1249 log: evm_coder::ethereum::Log,1250 ) -> DispatchResult {1251 let is_collection_admin = collection.is_owner_or_admin(sender);1252 let permissions = Self::property_permissions(collection.id);12531254 let mut token_owner_result = None;1255 let mut is_token_owner = || -> Result<bool, DispatchError> {1256 *token_owner_result.get_or_insert_with(&is_token_owner)1257 };12581259 for (key, value) in properties_updates {1260 let permission = permissions1261 .get(&key)1262 .cloned()1263 .unwrap_or_else(PropertyPermission::none);12641265 let is_property_exists = stored_properties.get(&key).is_some();12661267 match permission {1268 PropertyPermission { mutable: false, .. } if is_property_exists => {1269 return Err(<Error<T>>::NoPermission.into());1270 }12711272 PropertyPermission {1273 collection_admin,1274 token_owner,1275 ..1276 } => {1277 1278 let is_token_create =1279 is_token_create && (collection_admin || token_owner) && value.is_some();1280 if !(is_token_create1281 || (collection_admin && is_collection_admin)1282 || (token_owner && is_token_owner()?))1283 {1284 fail!(<Error<T>>::NoPermission);1285 }1286 }1287 }12881289 match value {1290 Some(value) => {1291 stored_properties1292 .try_set(key.clone(), value)1293 .map_err(<Error<T>>::from)?;12941295 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1296 }1297 None => {1298 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12991300 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1301 }1302 }13031304 <PalletEvm<T>>::deposit_log(log.clone());1305 }13061307 set_token_properties(stored_properties);13081309 Ok(())1310 }13111312 1313 1314 1315 1316 1317 1318 pub fn set_allowance_for_all(1319 collection: &CollectionHandle<T>,1320 owner: &T::CrossAccountId,1321 operator: &T::CrossAccountId,1322 approve: bool,1323 set_allowance: impl FnOnce(),1324 log: evm_coder::ethereum::Log,1325 ) -> DispatchResult {1326 if collection.permissions.access() == AccessMode::AllowList {1327 collection.check_allowlist(owner)?;1328 collection.check_allowlist(operator)?;1329 }13301331 Self::ensure_correct_receiver(operator)?;13321333 set_allowance();13341335 <PalletEvm<T>>::deposit_log(log);1336 Self::deposit_event(Event::ApprovedForAll(1337 collection.id,1338 owner.clone(),1339 operator.clone(),1340 approve,1341 ));1342 Ok(())1343 }13441345 1346 1347 1348 1349 1350 pub fn set_collection_property(1351 collection: &CollectionHandle<T>,1352 sender: &T::CrossAccountId,1353 property: Property,1354 ) -> DispatchResult {1355 Self::set_collection_properties(collection, sender, [property].into_iter())1356 }13571358 1359 1360 1361 1362 1363 1364 pub fn set_scoped_collection_property(1365 collection_id: CollectionId,1366 scope: PropertyScope,1367 property: Property,1368 ) -> DispatchResult {1369 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1370 properties.try_scoped_set(scope, property.key, property.value)1371 })1372 .map_err(<Error<T>>::from)?;13731374 Ok(())1375 }13761377 1378 1379 1380 1381 1382 1383 pub fn set_scoped_collection_properties(1384 collection_id: CollectionId,1385 scope: PropertyScope,1386 properties: impl Iterator<Item = Property>,1387 ) -> DispatchResult {1388 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1389 stored_properties.try_scoped_set_from_iter(scope, properties)1390 })1391 .map_err(<Error<T>>::from)?;13921393 Ok(())1394 }13951396 1397 1398 1399 1400 1401 pub fn set_collection_properties(1402 collection: &CollectionHandle<T>,1403 sender: &T::CrossAccountId,1404 properties: impl Iterator<Item = Property>,1405 ) -> DispatchResult {1406 Self::modify_collection_properties(1407 collection,1408 sender,1409 properties.map(|property| (property.key, Some(property.value))),1410 )1411 }14121413 1414 1415 1416 1417 1418 pub fn delete_collection_property(1419 collection: &CollectionHandle<T>,1420 sender: &T::CrossAccountId,1421 property_key: PropertyKey,1422 ) -> DispatchResult {1423 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1424 }14251426 1427 1428 1429 1430 1431 pub fn delete_collection_properties(1432 collection: &CollectionHandle<T>,1433 sender: &T::CrossAccountId,1434 property_keys: impl Iterator<Item = PropertyKey>,1435 ) -> DispatchResult {1436 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1437 }14381439 1440 1441 1442 1443 1444 1445 pub fn set_property_permission_unchecked(1446 collection: CollectionId,1447 property_permission: PropertyKeyPermission,1448 ) -> DispatchResult {1449 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1450 permissions.try_set(property_permission.key, property_permission.permission)1451 })1452 .map_err(<Error<T>>::from)?;1453 Ok(())1454 }14551456 1457 1458 1459 1460 1461 pub fn set_property_permission(1462 collection: &CollectionHandle<T>,1463 sender: &T::CrossAccountId,1464 property_permission: PropertyKeyPermission,1465 ) -> DispatchResult {1466 Self::set_scoped_property_permission(1467 collection,1468 sender,1469 PropertyScope::None,1470 property_permission,1471 )1472 }14731474 1475 1476 1477 1478 1479 1480 pub fn set_scoped_property_permission(1481 collection: &CollectionHandle<T>,1482 sender: &T::CrossAccountId,1483 scope: PropertyScope,1484 property_permission: PropertyKeyPermission,1485 ) -> DispatchResult {1486 collection.check_is_owner_or_admin(sender)?;14871488 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1489 let current_permission = all_permissions.get(&property_permission.key);1490 if matches![1491 current_permission,1492 Some(PropertyPermission { mutable: false, .. })1493 ] {1494 return Err(<Error<T>>::NoPermission.into());1495 }14961497 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1498 let property_permission = property_permission.clone();1499 permissions.try_scoped_set(1500 scope,1501 property_permission.key,1502 property_permission.permission,1503 )1504 })1505 .map_err(<Error<T>>::from)?;15061507 Self::deposit_event(Event::PropertyPermissionSet(1508 collection.id,1509 property_permission.key,1510 ));1511 <PalletEvm<T>>::deposit_log(1512 erc::CollectionHelpersEvents::CollectionChanged {1513 collection_id: eth::collection_id_to_address(collection.id),1514 }1515 .to_log(T::ContractAddress::get()),1516 );15171518 Ok(())1519 }15201521 1522 1523 1524 1525 1526 #[transactional]1527 pub fn set_token_property_permissions(1528 collection: &CollectionHandle<T>,1529 sender: &T::CrossAccountId,1530 property_permissions: Vec<PropertyKeyPermission>,1531 ) -> DispatchResult {1532 Self::set_scoped_token_property_permissions(1533 collection,1534 sender,1535 PropertyScope::None,1536 property_permissions,1537 )1538 }15391540 1541 1542 1543 1544 1545 1546 #[transactional]1547 pub fn set_scoped_token_property_permissions(1548 collection: &CollectionHandle<T>,1549 sender: &T::CrossAccountId,1550 scope: PropertyScope,1551 property_permissions: Vec<PropertyKeyPermission>,1552 ) -> DispatchResult {1553 for prop_pemission in property_permissions {1554 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1555 }15561557 Ok(())1558 }15591560 1561 pub fn get_collection_property(1562 collection_id: CollectionId,1563 key: &PropertyKey,1564 ) -> Option<PropertyValue> {1565 Self::collection_properties(collection_id).get(key).cloned()1566 }15671568 1569 pub fn bytes_keys_to_property_keys(1570 keys: Vec<Vec<u8>>,1571 ) -> Result<Vec<PropertyKey>, DispatchError> {1572 keys.into_iter()1573 .map(|key| -> Result<PropertyKey, DispatchError> {1574 key.try_into()1575 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1576 })1577 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1578 }15791580 1581 pub fn filter_collection_properties(1582 collection_id: CollectionId,1583 keys: Option<Vec<PropertyKey>>,1584 ) -> Result<Vec<Property>, DispatchError> {1585 let properties = Self::collection_properties(collection_id);15861587 let properties = keys1588 .map(|keys| {1589 keys.into_iter()1590 .filter_map(|key| {1591 properties.get(&key).map(|value| Property {1592 key,1593 value: value.clone(),1594 })1595 })1596 .collect()1597 })1598 .unwrap_or_else(|| {1599 properties1600 .into_iter()1601 .map(|(key, value)| Property { key, value })1602 .collect()1603 });16041605 Ok(properties)1606 }16071608 1609 pub fn filter_property_permissions(1610 collection_id: CollectionId,1611 keys: Option<Vec<PropertyKey>>,1612 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1613 let permissions = Self::property_permissions(collection_id);16141615 let key_permissions = keys1616 .map(|keys| {1617 keys.into_iter()1618 .filter_map(|key| {1619 permissions1620 .get(&key)1621 .map(|permission| PropertyKeyPermission {1622 key,1623 permission: permission.clone(),1624 })1625 })1626 .collect()1627 })1628 .unwrap_or_else(|| {1629 permissions1630 .into_iter()1631 .map(|(key, permission)| PropertyKeyPermission { key, permission })1632 .collect()1633 });16341635 Ok(key_permissions)1636 }16371638 1639 1640 1641 pub fn toggle_allowlist(1642 collection: &CollectionHandle<T>,1643 sender: &T::CrossAccountId,1644 user: &T::CrossAccountId,1645 allowed: bool,1646 ) -> DispatchResult {1647 collection.check_is_owner_or_admin(sender)?;16481649 16501651 if allowed {1652 <Allowlist<T>>::insert((collection.id, user), true);1653 Self::deposit_event(Event::<T>::AllowListAddressAdded(1654 collection.id,1655 user.clone(),1656 ));1657 } else {1658 <Allowlist<T>>::remove((collection.id, user));1659 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1660 collection.id,1661 user.clone(),1662 ));1663 }16641665 <PalletEvm<T>>::deposit_log(1666 erc::CollectionHelpersEvents::CollectionChanged {1667 collection_id: eth::collection_id_to_address(collection.id),1668 }1669 .to_log(T::ContractAddress::get()),1670 );16711672 Ok(())1673 }16741675 1676 1677 1678 pub fn toggle_admin(1679 collection: &CollectionHandle<T>,1680 sender: &T::CrossAccountId,1681 user: &T::CrossAccountId,1682 admin: bool,1683 ) -> DispatchResult {1684 collection.check_is_internal()?;1685 collection.check_is_owner(sender)?;16861687 let is_admin = <IsAdmin<T>>::get((collection.id, user));1688 if is_admin == admin {1689 if admin {1690 return Ok(());1691 } else {1692 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1693 }1694 }1695 let amount = <AdminAmount<T>>::get(collection.id);16961697 16981699 if admin {1700 let amount = amount1701 .checked_add(1)1702 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1703 ensure!(1704 amount <= Self::collection_admins_limit(),1705 <Error<T>>::CollectionAdminCountExceeded,1706 );17071708 <AdminAmount<T>>::insert(collection.id, amount);1709 <IsAdmin<T>>::insert((collection.id, user), true);17101711 Self::deposit_event(Event::<T>::CollectionAdminAdded(1712 collection.id,1713 user.clone(),1714 ));1715 } else {1716 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1717 <IsAdmin<T>>::remove((collection.id, user));17181719 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1720 collection.id,1721 user.clone(),1722 ));1723 }17241725 <PalletEvm<T>>::deposit_log(1726 erc::CollectionHelpersEvents::CollectionChanged {1727 collection_id: eth::collection_id_to_address(collection.id),1728 }1729 .to_log(T::ContractAddress::get()),1730 );17311732 Ok(())1733 }17341735 1736 pub fn update_limits(1737 user: &T::CrossAccountId,1738 collection: &mut CollectionHandle<T>,1739 new_limit: CollectionLimits,1740 ) -> DispatchResult {1741 collection.check_is_internal()?;1742 collection.check_is_owner_or_admin(user)?;17431744 collection.limits =1745 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;17461747 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1748 <PalletEvm<T>>::deposit_log(1749 erc::CollectionHelpersEvents::CollectionChanged {1750 collection_id: eth::collection_id_to_address(collection.id),1751 }1752 .to_log(T::ContractAddress::get()),1753 );17541755 collection.save()1756 }17571758 1759 fn clamp_limits(1760 mode: CollectionMode,1761 old_limit: &CollectionLimits,1762 mut new_limit: CollectionLimits,1763 ) -> Result<CollectionLimits, DispatchError> {1764 let limits = old_limit;1765 limit_default!(old_limit, new_limit,1766 account_token_ownership_limit => ensure!(1767 new_limit <= MAX_TOKEN_OWNERSHIP,1768 <Error<T>>::CollectionLimitBoundsExceeded,1769 ),1770 sponsored_data_size => ensure!(1771 new_limit <= CUSTOM_DATA_LIMIT,1772 <Error<T>>::CollectionLimitBoundsExceeded,1773 ),17741775 sponsored_data_rate_limit => {},1776 token_limit => ensure!(1777 old_limit >= new_limit && new_limit > 0,1778 <Error<T>>::CollectionTokenLimitExceeded1779 ),17801781 sponsor_transfer_timeout(match mode {1782 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1783 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1784 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1785 }) => ensure!(1786 new_limit <= MAX_SPONSOR_TIMEOUT,1787 <Error<T>>::CollectionLimitBoundsExceeded,1788 ),1789 sponsor_approve_timeout => {},1790 owner_can_transfer => ensure!(1791 !limits.owner_can_transfer_instaled() ||1792 old_limit || !new_limit,1793 <Error<T>>::OwnerPermissionsCantBeReverted,1794 ),1795 owner_can_destroy => ensure!(1796 old_limit || !new_limit,1797 <Error<T>>::OwnerPermissionsCantBeReverted,1798 ),1799 transfers_enabled => {},1800 );1801 Ok(new_limit)1802 }18031804 1805 pub fn update_permissions(1806 user: &T::CrossAccountId,1807 collection: &mut CollectionHandle<T>,1808 new_permission: CollectionPermissions,1809 ) -> DispatchResult {1810 collection.check_is_internal()?;1811 collection.check_is_owner_or_admin(user)?;1812 collection.permissions = Self::clamp_permissions(1813 collection.mode.clone(),1814 &collection.permissions,1815 new_permission,1816 )?;18171818 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1819 <PalletEvm<T>>::deposit_log(1820 erc::CollectionHelpersEvents::CollectionChanged {1821 collection_id: eth::collection_id_to_address(collection.id),1822 }1823 .to_log(T::ContractAddress::get()),1824 );18251826 collection.save()1827 }18281829 1830 fn clamp_permissions(1831 _mode: CollectionMode,1832 old_permission: &CollectionPermissions,1833 mut new_permission: CollectionPermissions,1834 ) -> Result<CollectionPermissions, DispatchError> {1835 limit_default_clone!(old_permission, new_permission,1836 access => {},1837 mint_mode => {},1838 nesting => { },1839 );1840 Ok(new_permission)1841 }18421843 1844 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1845 CollectionProperties::<T>::mutate(collection_id, |properties| {1846 properties.recompute_consumed_space();1847 });18481849 Ok(())1850 }1851}185218531854#[macro_export]1855macro_rules! unsupported {1856 ($runtime:path) => {1857 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1858 };1859}186018611862pub trait CommonWeightInfo<CrossAccountId> {1863 1864 fn create_item(data: &CreateItemData) -> Weight {1865 Self::create_multiple_items(from_ref(data))1866 }18671868 1869 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;18701871 1872 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;18731874 1875 fn burn_item() -> Weight;18761877 1878 1879 1880 fn set_collection_properties(amount: u32) -> Weight;18811882 1883 1884 1885 fn delete_collection_properties(amount: u32) -> Weight;18861887 1888 1889 1890 fn set_token_properties(amount: u32) -> Weight;18911892 1893 1894 1895 fn delete_token_properties(amount: u32) -> Weight;18961897 1898 1899 1900 fn set_token_property_permissions(amount: u32) -> Weight;19011902 1903 fn transfer() -> Weight;19041905 1906 fn approve() -> Weight;19071908 1909 fn approve_from() -> Weight;19101911 1912 fn transfer_from() -> Weight;19131914 1915 fn burn_from() -> Weight;19161917 1918 1919 1920 1921 fn burn_recursively_self_raw() -> Weight;19221923 1924 1925 1926 fn burn_recursively_breadth_raw(amount: u32) -> Weight;19271928 1929 1930 1931 1932 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1933 Self::burn_recursively_self_raw()1934 .saturating_mul(max_selfs.max(1) as u64)1935 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1936 }19371938 1939 fn token_owner() -> Weight;19401941 1942 fn set_allowance_for_all() -> Weight;19431944 1945 fn force_repair_item() -> Weight;1946}194719481949pub trait RefungibleExtensionsWeightInfo {1950 1951 fn repartition() -> Weight;1952}195319541955195619571958pub trait CommonCollectionOperations<T: Config> {1959 1960 1961 1962 1963 1964 1965 fn create_item(1966 &self,1967 sender: T::CrossAccountId,1968 to: T::CrossAccountId,1969 data: CreateItemData,1970 nesting_budget: &dyn Budget,1971 ) -> DispatchResultWithPostInfo;19721973 1974 1975 1976 1977 1978 1979 fn create_multiple_items(1980 &self,1981 sender: T::CrossAccountId,1982 to: T::CrossAccountId,1983 data: Vec<CreateItemData>,1984 nesting_budget: &dyn Budget,1985 ) -> DispatchResultWithPostInfo;19861987 1988 1989 1990 1991 1992 1993 fn create_multiple_items_ex(1994 &self,1995 sender: T::CrossAccountId,1996 data: CreateItemExData<T::CrossAccountId>,1997 nesting_budget: &dyn Budget,1998 ) -> DispatchResultWithPostInfo;19992000 2001 2002 2003 2004 2005 fn burn_item(2006 &self,2007 sender: T::CrossAccountId,2008 token: TokenId,2009 amount: u128,2010 ) -> DispatchResultWithPostInfo;20112012 2013 2014 2015 2016 2017 2018 fn burn_item_recursively(2019 &self,2020 sender: T::CrossAccountId,2021 token: TokenId,2022 self_budget: &dyn Budget,2023 breadth_budget: &dyn Budget,2024 ) -> DispatchResultWithPostInfo;20252026 2027 2028 2029 2030 fn set_collection_properties(2031 &self,2032 sender: T::CrossAccountId,2033 properties: Vec<Property>,2034 ) -> DispatchResultWithPostInfo;20352036 2037 2038 2039 2040 fn delete_collection_properties(2041 &self,2042 sender: &T::CrossAccountId,2043 property_keys: Vec<PropertyKey>,2044 ) -> DispatchResultWithPostInfo;20452046 2047 2048 2049 2050 2051 2052 2053 2054 2055 fn set_token_properties(2056 &self,2057 sender: T::CrossAccountId,2058 token_id: TokenId,2059 properties: Vec<Property>,2060 budget: &dyn Budget,2061 ) -> DispatchResultWithPostInfo;20622063 2064 2065 2066 2067 2068 2069 2070 2071 2072 fn delete_token_properties(2073 &self,2074 sender: T::CrossAccountId,2075 token_id: TokenId,2076 property_keys: Vec<PropertyKey>,2077 budget: &dyn Budget,2078 ) -> DispatchResultWithPostInfo;20792080 2081 2082 2083 2084 2085 2086 fn set_token_property_permissions(2087 &self,2088 sender: &T::CrossAccountId,2089 property_permissions: Vec<PropertyKeyPermission>,2090 ) -> DispatchResultWithPostInfo;20912092 2093 2094 2095 2096 2097 2098 2099 fn transfer(2100 &self,2101 sender: T::CrossAccountId,2102 to: T::CrossAccountId,2103 token: TokenId,2104 amount: u128,2105 budget: &dyn Budget,2106 ) -> DispatchResultWithPostInfo;21072108 2109 2110 2111 2112 2113 2114 fn approve(2115 &self,2116 sender: T::CrossAccountId,2117 spender: T::CrossAccountId,2118 token: TokenId,2119 amount: u128,2120 ) -> DispatchResultWithPostInfo;21212122 2123 2124 2125 2126 2127 2128 2129 fn approve_from(2130 &self,2131 sender: T::CrossAccountId,2132 from: T::CrossAccountId,2133 to: T::CrossAccountId,2134 token: TokenId,2135 amount: u128,2136 ) -> DispatchResultWithPostInfo;21372138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 fn transfer_from(2149 &self,2150 sender: T::CrossAccountId,2151 from: T::CrossAccountId,2152 to: T::CrossAccountId,2153 token: TokenId,2154 amount: u128,2155 budget: &dyn Budget,2156 ) -> DispatchResultWithPostInfo;21572158 2159 2160 2161 2162 2163 2164 2165 2166 2167 fn burn_from(2168 &self,2169 sender: T::CrossAccountId,2170 from: T::CrossAccountId,2171 token: TokenId,2172 amount: u128,2173 budget: &dyn Budget,2174 ) -> DispatchResultWithPostInfo;21752176 2177 2178 2179 2180 2181 2182 fn check_nesting(2183 &self,2184 sender: T::CrossAccountId,2185 from: (CollectionId, TokenId),2186 under: TokenId,2187 budget: &dyn Budget,2188 ) -> DispatchResult;21892190 2191 2192 2193 2194 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21952196 2197 2198 2199 2200 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22012202 2203 2204 2205 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;22062207 2208 fn collection_tokens(&self) -> Vec<TokenId>;22092210 2211 2212 2213 fn token_exists(&self, token: TokenId) -> bool;22142215 2216 fn last_token_id(&self) -> TokenId;22172218 2219 2220 2221 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22222223 2224 2225 2226 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22272228 2229 2230 2231 2232 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22332234 2235 2236 2237 2238 2239 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22402241 2242 fn total_supply(&self) -> u32;22432244 2245 2246 2247 fn account_balance(&self, account: T::CrossAccountId) -> u32;22482249 2250 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;22512252 2253 fn total_pieces(&self, token: TokenId) -> Option<u128>;22542255 2256 2257 2258 2259 2260 fn allowance(2261 &self,2262 sender: T::CrossAccountId,2263 spender: T::CrossAccountId,2264 token: TokenId,2265 ) -> u128;22662267 2268 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;22692270 2271 2272 2273 2274 fn set_allowance_for_all(2275 &self,2276 owner: T::CrossAccountId,2277 operator: T::CrossAccountId,2278 approve: bool,2279 ) -> DispatchResultWithPostInfo;22802281 2282 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;22832284 2285 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2286}228722882289pub trait RefungibleExtensions<T>2290where2291 T: Config,2292{2293 2294 2295 2296 2297 2298 2299 2300 fn repartition(2301 &self,2302 sender: &T::CrossAccountId,2303 token: TokenId,2304 amount: u128,2305 ) -> DispatchResultWithPostInfo;2306}23072308230923102311pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2312 let post_info = PostDispatchInfo {2313 actual_weight: Some(weight),2314 pays_fee: Pays::Yes,2315 };2316 match res {2317 Ok(()) => Ok(post_info),2318 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2319 }2320}23212322impl<T: Config> From<PropertiesError> for Error<T> {2323 fn from(error: PropertiesError) -> Self {2324 match error {2325 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2326 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2327 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2328 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2329 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2330 }2331 }2332}