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 core::marker::PhantomData;424425 use super::*;426 use dispatch::CollectionDispatch;427 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};428 use frame_support::traits::Currency;429 use up_data_structs::{TokenId, mapping::TokenAddressMapping};430 use scale_info::TypeInfo;431 use weights::WeightInfo;432433 #[pallet::config]434 pub trait Config:435 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo436 {437 438 type WeightInfo: WeightInfo;439440 441 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;442443 444 type Currency: Currency<Self::AccountId>;445446 447 #[pallet::constant]448 type CollectionCreationPrice: Get<449 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,450 >;451452 453 type CollectionDispatch: CollectionDispatch<Self>;454455 456 type TreasuryAccountId: Get<Self::AccountId>;457458 459 #[pallet::constant]460 type ContractAddress: Get<H160>;461462 463 type EvmTokenAddressMapping: TokenAddressMapping<H160>;464465 466 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;467 }468469 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);470471 #[pallet::pallet]472 #[pallet::storage_version(STORAGE_VERSION)]473 pub struct Pallet<T>(_);474475 #[pallet::extra_constants]476 impl<T: Config> Pallet<T> {477 478 pub fn collection_admins_limit() -> u32 {479 COLLECTION_ADMINS_LIMIT480 }481 }482483 #[pallet::genesis_config]484 pub struct GenesisConfig<T>(PhantomData<T>);485486 #[cfg(feature = "std")]487 impl<T: Config> Default for GenesisConfig<T> {488 fn default() -> Self {489 Self(Default::default())490 }491 }492493 #[pallet::genesis_build]494 impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {495 fn build(&self) {496 StorageVersion::new(1).put::<Pallet<T>>();497 }498 }499 impl<T: Config> Pallet<T> {500 501 pub fn deposit_event(event: Event<T>) {502 let event = <T as Config>::RuntimeEvent::from(event);503 let event = event.into();504 <frame_system::Pallet<T>>::deposit_event(event)505 }506 }507508 #[pallet::event]509 pub enum Event<T: Config> {510 511 CollectionCreated(512 513 CollectionId,514 515 u8,516 517 T::AccountId,518 ),519520 521 CollectionDestroyed(522 523 CollectionId,524 ),525526 527 ItemCreated(528 529 CollectionId,530 531 TokenId,532 533 T::CrossAccountId,534 535 u128,536 ),537538 539 ItemDestroyed(540 541 CollectionId,542 543 TokenId,544 545 T::CrossAccountId,546 547 u128,548 ),549550 551 Transfer(552 553 CollectionId,554 555 TokenId,556 557 T::CrossAccountId,558 559 T::CrossAccountId,560 561 u128,562 ),563564 565 Approved(566 567 CollectionId,568 569 TokenId,570 571 T::CrossAccountId,572 573 T::CrossAccountId,574 575 u128,576 ),577578 579 ApprovedForAll(580 581 CollectionId,582 583 T::CrossAccountId,584 585 T::CrossAccountId,586 587 bool,588 ),589590 591 CollectionPropertySet(592 593 CollectionId,594 595 PropertyKey,596 ),597598 599 CollectionPropertyDeleted(600 601 CollectionId,602 603 PropertyKey,604 ),605606 607 TokenPropertySet(608 609 CollectionId,610 611 TokenId,612 613 PropertyKey,614 ),615616 617 TokenPropertyDeleted(618 619 CollectionId,620 621 TokenId,622 623 PropertyKey,624 ),625626 627 PropertyPermissionSet(628 629 CollectionId,630 631 PropertyKey,632 ),633634 635 AllowListAddressAdded(636 637 CollectionId,638 639 T::CrossAccountId,640 ),641642 643 AllowListAddressRemoved(644 645 CollectionId,646 647 T::CrossAccountId,648 ),649650 651 CollectionAdminAdded(652 653 CollectionId,654 655 T::CrossAccountId,656 ),657658 659 CollectionAdminRemoved(660 661 CollectionId,662 663 T::CrossAccountId,664 ),665666 667 CollectionLimitSet(668 669 CollectionId,670 ),671672 673 CollectionOwnerChanged(674 675 CollectionId,676 677 T::AccountId,678 ),679680 681 CollectionPermissionSet(682 683 CollectionId,684 ),685686 687 CollectionSponsorSet(688 689 CollectionId,690 691 T::AccountId,692 ),693694 695 SponsorshipConfirmed(696 697 CollectionId,698 699 T::AccountId,700 ),701702 703 CollectionSponsorRemoved(704 705 CollectionId,706 ),707 }708709 #[pallet::error]710 pub enum Error<T> {711 712 CollectionNotFound,713 714 MustBeTokenOwner,715 716 NoPermission,717 718 CantDestroyNotEmptyCollection,719 720 PublicMintingNotAllowed,721 722 AddressNotInAllowlist,723724 725 CollectionNameLimitExceeded,726 727 CollectionDescriptionLimitExceeded,728 729 CollectionTokenPrefixLimitExceeded,730 731 TotalCollectionsLimitExceeded,732 733 CollectionAdminCountExceeded,734 735 CollectionLimitBoundsExceeded,736 737 OwnerPermissionsCantBeReverted,738 739 TransferNotAllowed,740 741 AccountTokenLimitExceeded,742 743 CollectionTokenLimitExceeded,744 745 MetadataFlagFrozen,746747 748 TokenNotFound,749 750 TokenValueTooLow,751 752 ApprovedValueTooLow,753 754 CantApproveMoreThanOwned,755 756 AddressIsNotEthMirror,757758 759 AddressIsZero,760761 762 UnsupportedOperation,763764 765 NotSufficientFounds,766767 768 UserIsNotAllowedToNest,769 770 SourceCollectionIsNotAllowedToNest,771772 773 CollectionFieldSizeExceeded,774775 776 NoSpaceForProperty,777778 779 PropertyLimitReached,780781 782 PropertyKeyIsTooLong,783784 785 InvalidCharacterInPropertyKey,786787 788 EmptyPropertyKey,789790 791 CollectionIsExternal,792793 794 CollectionIsInternal,795796 797 ConfirmSponsorshipFail,798799 800 UserIsNotCollectionAdmin,801 }802803 804 #[pallet::storage]805 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;806807 808 #[pallet::storage]809 pub type DestroyedCollectionCount<T> =810 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;811812 813 #[pallet::storage]814 pub type CollectionById<T> = StorageMap<815 Hasher = Blake2_128Concat,816 Key = CollectionId,817 Value = Collection<<T as frame_system::Config>::AccountId>,818 QueryKind = OptionQuery,819 >;820821 822 #[pallet::storage]823 #[pallet::getter(fn collection_properties)]824 pub type CollectionProperties<T> = StorageMap<825 Hasher = Blake2_128Concat,826 Key = CollectionId,827 Value = CollectionPropertiesT,828 QueryKind = ValueQuery,829 >;830831 832 #[pallet::storage]833 #[pallet::getter(fn property_permissions)]834 pub type CollectionPropertyPermissions<T> = StorageMap<835 Hasher = Blake2_128Concat,836 Key = CollectionId,837 Value = PropertiesPermissionMap,838 QueryKind = ValueQuery,839 >;840841 842 #[pallet::storage]843 pub type AdminAmount<T> = StorageMap<844 Hasher = Blake2_128Concat,845 Key = CollectionId,846 Value = u32,847 QueryKind = ValueQuery,848 >;849850 851 #[pallet::storage]852 pub type IsAdmin<T: Config> = StorageNMap<853 Key = (854 Key<Blake2_128Concat, CollectionId>,855 Key<Blake2_128Concat, T::CrossAccountId>,856 ),857 Value = bool,858 QueryKind = ValueQuery,859 >;860861 862 #[pallet::storage]863 pub type Allowlist<T: Config> = StorageNMap<864 Key = (865 Key<Blake2_128Concat, CollectionId>,866 Key<Blake2_128Concat, T::CrossAccountId>,867 ),868 Value = bool,869 QueryKind = ValueQuery,870 >;871872 873 #[pallet::storage]874 pub type DummyStorageValue<T: Config> = StorageValue<875 Value = (876 CollectionStats,877 CollectionId,878 TokenId,879 TokenChild,880 PhantomType<(881 TokenData<T::CrossAccountId>,882 RpcCollection<T::AccountId>,883 884 PovInfo,885 )>,886 ),887 QueryKind = OptionQuery,888 >;889}890891impl<T: Config> Pallet<T> {892 893 894 895 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {896 ensure!(897 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,898 <Error<T>>::AddressIsZero899 );900 Ok(())901 }902903 904 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {905 <IsAdmin<T>>::iter_prefix((collection,))906 .map(|(a, _)| a)907 .collect()908 }909910 911 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {912 <Allowlist<T>>::iter_prefix((collection,))913 .map(|(a, _)| a)914 .collect()915 }916917 918 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {919 <Allowlist<T>>::get((collection, user))920 }921922 923 pub fn collection_stats() -> CollectionStats {924 let created = <CreatedCollectionCount<T>>::get();925 let destroyed = <DestroyedCollectionCount<T>>::get();926 CollectionStats {927 created: created.0,928 destroyed: destroyed.0,929 alive: created.0 - destroyed.0,930 }931 }932933 934 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {935 let collection = <CollectionById<T>>::get(collection)?;936 let limits = collection.limits;937 let effective_limits = CollectionLimits {938 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),939 sponsored_data_size: Some(limits.sponsored_data_size()),940 sponsored_data_rate_limit: Some(941 limits942 .sponsored_data_rate_limit943 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),944 ),945 token_limit: Some(limits.token_limit()),946 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(947 match collection.mode {948 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,949 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,950 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,951 },952 )),953 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),954 owner_can_transfer: Some(limits.owner_can_transfer()),955 owner_can_destroy: Some(limits.owner_can_destroy()),956 transfers_enabled: Some(limits.transfers_enabled()),957 };958959 Some(effective_limits)960 }961962 963 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {964 let Collection {965 name,966 description,967 owner,968 mode,969 token_prefix,970 sponsorship,971 limits,972 permissions,973 flags,974 } = <CollectionById<T>>::get(collection)?;975976 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)977 .into_iter()978 .map(|(key, permission)| PropertyKeyPermission { key, permission })979 .collect();980981 let properties = <CollectionProperties<T>>::get(collection)982 .into_iter()983 .map(|(key, value)| Property { key, value })984 .collect();985986 let permissions = CollectionPermissions {987 access: Some(permissions.access()),988 mint_mode: Some(permissions.mint_mode()),989 nesting: Some(permissions.nesting().clone()),990 };991992 Some(RpcCollection {993 name: name.into_inner(),994 description: description.into_inner(),995 owner,996 mode,997 token_prefix: token_prefix.into_inner(),998 sponsorship,999 limits,1000 permissions,1001 token_property_permissions,1002 properties,1003 read_only: flags.external,10041005 flags: RpcCollectionFlags {1006 foreign: flags.foreign,1007 erc721metadata: flags.erc721metadata,1008 },1009 })1010 }1011}10121013macro_rules! limit_default {1014 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1015 $(1016 if let Some($new) = $new.$field {1017 let $old = $old.$field($($arg)?);1018 let _ = $new;1019 let _ = $old;1020 $check1021 } else {1022 $new.$field = $old.$field1023 }1024 )*1025 }};1026}1027macro_rules! limit_default_clone {1028 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1029 $(1030 if let Some($new) = $new.$field.clone() {1031 let $old = $old.$field($($arg)?);1032 let _ = $new;1033 let _ = $old;1034 $check1035 } else {1036 $new.$field = $old.$field.clone()1037 }1038 )*1039 }};1040}10411042impl<T: Config> Pallet<T> {1043 1044 1045 1046 1047 1048 pub fn init_collection(1049 owner: T::CrossAccountId,1050 payer: T::CrossAccountId,1051 data: CreateCollectionData<T::AccountId>,1052 flags: CollectionFlags,1053 ) -> Result<CollectionId, DispatchError> {1054 {1055 ensure!(1056 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1057 Error::<T>::CollectionTokenPrefixLimitExceeded1058 );1059 }10601061 let created_count = <CreatedCollectionCount<T>>::get()1062 .01063 .checked_add(1)1064 .ok_or(ArithmeticError::Overflow)?;1065 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1066 let id = CollectionId(created_count);10671068 1069 ensure!(1070 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1071 <Error<T>>::TotalCollectionsLimitExceeded1072 );10731074 10751076 let collection = Collection {1077 owner: owner.as_sub().clone(),1078 name: data.name,1079 mode: data.mode.clone(),1080 description: data.description,1081 token_prefix: data.token_prefix,1082 sponsorship: data1083 .pending_sponsor1084 .map(SponsorshipState::Unconfirmed)1085 .unwrap_or_default(),1086 limits: data1087 .limits1088 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1089 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1090 permissions: data1091 .permissions1092 .map(|permissions| {1093 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1094 })1095 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1096 flags,1097 };10981099 let mut collection_properties = CollectionPropertiesT::new();1100 collection_properties1101 .try_set_from_iter(data.properties.into_iter())1102 .map_err(<Error<T>>::from)?;11031104 CollectionProperties::<T>::insert(id, collection_properties);11051106 let mut token_props_permissions = PropertiesPermissionMap::new();1107 token_props_permissions1108 .try_set_from_iter(data.token_property_permissions.into_iter())1109 .map_err(<Error<T>>::from)?;11101111 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11121113 1114 {1115 let mut imbalance =1116 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1117 imbalance.subsume(1118 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1119 &T::TreasuryAccountId::get(),1120 T::CollectionCreationPrice::get(),1121 ),1122 );1123 <T as Config>::Currency::settle(1124 payer.as_sub(),1125 imbalance,1126 WithdrawReasons::TRANSFER,1127 ExistenceRequirement::KeepAlive,1128 )1129 .map_err(|_| Error::<T>::NotSufficientFounds)?;1130 }11311132 <CreatedCollectionCount<T>>::put(created_count);1133 <Pallet<T>>::deposit_event(Event::CollectionCreated(1134 id,1135 data.mode.id(),1136 owner.as_sub().clone(),1137 ));1138 <PalletEvm<T>>::deposit_log(1139 erc::CollectionHelpersEvents::CollectionCreated {1140 owner: *owner.as_eth(),1141 collection_id: eth::collection_id_to_address(id),1142 }1143 .to_log(T::ContractAddress::get()),1144 );1145 <CollectionById<T>>::insert(id, collection);1146 Ok(id)1147 }11481149 1150 1151 1152 1153 pub fn destroy_collection(1154 collection: CollectionHandle<T>,1155 sender: &T::CrossAccountId,1156 ) -> DispatchResult {1157 ensure!(1158 collection.limits.owner_can_destroy(),1159 <Error<T>>::NoPermission,1160 );1161 collection.check_is_owner(sender)?;11621163 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1164 .01165 .checked_add(1)1166 .ok_or(ArithmeticError::Overflow)?;11671168 11691170 <DestroyedCollectionCount<T>>::put(destroyed_collections);1171 <CollectionById<T>>::remove(collection.id);1172 <AdminAmount<T>>::remove(collection.id);1173 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1174 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1175 <CollectionProperties<T>>::remove(collection.id);11761177 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11781179 <PalletEvm<T>>::deposit_log(1180 erc::CollectionHelpersEvents::CollectionDestroyed {1181 collection_id: eth::collection_id_to_address(collection.id),1182 }1183 .to_log(T::ContractAddress::get()),1184 );1185 Ok(())1186 }11871188 1189 1190 1191 1192 1193 1194 1195 1196 #[transactional]1197 fn modify_collection_properties(1198 collection: &CollectionHandle<T>,1199 sender: &T::CrossAccountId,1200 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1201 ) -> DispatchResult {1202 collection.check_is_owner_or_admin(sender)?;12031204 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12051206 for (key, value) in properties_updates {1207 match value {1208 Some(value) => {1209 stored_properties1210 .try_set(key.clone(), value)1211 .map_err(<Error<T>>::from)?;12121213 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1214 <PalletEvm<T>>::deposit_log(1215 erc::CollectionHelpersEvents::CollectionChanged {1216 collection_id: eth::collection_id_to_address(collection.id),1217 }1218 .to_log(T::ContractAddress::get()),1219 );1220 }1221 None => {1222 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12231224 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1225 <PalletEvm<T>>::deposit_log(1226 erc::CollectionHelpersEvents::CollectionChanged {1227 collection_id: eth::collection_id_to_address(collection.id),1228 }1229 .to_log(T::ContractAddress::get()),1230 );1231 }1232 }1233 }12341235 <CollectionProperties<T>>::set(collection.id, stored_properties);12361237 Ok(())1238 }12391240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 pub fn modify_token_properties(1258 collection: &CollectionHandle<T>,1259 sender: &T::CrossAccountId,1260 token_id: TokenId,1261 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1262 is_token_create: bool,1263 mut stored_properties: TokenProperties,1264 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1265 set_token_properties: impl FnOnce(TokenProperties),1266 log: evm_coder::ethereum::Log,1267 ) -> DispatchResult {1268 let is_collection_admin = collection.is_owner_or_admin(sender);1269 let permissions = Self::property_permissions(collection.id);12701271 let mut token_owner_result = None;1272 let mut is_token_owner = || -> Result<bool, DispatchError> {1273 *token_owner_result.get_or_insert_with(&is_token_owner)1274 };12751276 for (key, value) in properties_updates {1277 let permission = permissions1278 .get(&key)1279 .cloned()1280 .unwrap_or_else(PropertyPermission::none);12811282 let is_property_exists = stored_properties.get(&key).is_some();12831284 match permission {1285 PropertyPermission { mutable: false, .. } if is_property_exists => {1286 return Err(<Error<T>>::NoPermission.into());1287 }12881289 PropertyPermission {1290 collection_admin,1291 token_owner,1292 ..1293 } => {1294 1295 let is_token_create =1296 is_token_create && (collection_admin || token_owner) && value.is_some();1297 if !(is_token_create1298 || (collection_admin && is_collection_admin)1299 || (token_owner && is_token_owner()?))1300 {1301 fail!(<Error<T>>::NoPermission);1302 }1303 }1304 }13051306 match value {1307 Some(value) => {1308 stored_properties1309 .try_set(key.clone(), value)1310 .map_err(<Error<T>>::from)?;13111312 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1313 }1314 None => {1315 stored_properties.remove(&key).map_err(<Error<T>>::from)?;13161317 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1318 }1319 }13201321 <PalletEvm<T>>::deposit_log(log.clone());1322 }13231324 set_token_properties(stored_properties);13251326 Ok(())1327 }13281329 1330 1331 1332 1333 1334 1335 pub fn set_allowance_for_all(1336 collection: &CollectionHandle<T>,1337 owner: &T::CrossAccountId,1338 operator: &T::CrossAccountId,1339 approve: bool,1340 set_allowance: impl FnOnce(),1341 log: evm_coder::ethereum::Log,1342 ) -> DispatchResult {1343 if collection.permissions.access() == AccessMode::AllowList {1344 collection.check_allowlist(owner)?;1345 collection.check_allowlist(operator)?;1346 }13471348 Self::ensure_correct_receiver(operator)?;13491350 set_allowance();13511352 <PalletEvm<T>>::deposit_log(log);1353 Self::deposit_event(Event::ApprovedForAll(1354 collection.id,1355 owner.clone(),1356 operator.clone(),1357 approve,1358 ));1359 Ok(())1360 }13611362 1363 1364 1365 1366 1367 pub fn set_collection_property(1368 collection: &CollectionHandle<T>,1369 sender: &T::CrossAccountId,1370 property: Property,1371 ) -> DispatchResult {1372 Self::set_collection_properties(collection, sender, [property].into_iter())1373 }13741375 1376 1377 1378 1379 1380 1381 pub fn set_scoped_collection_property(1382 collection_id: CollectionId,1383 scope: PropertyScope,1384 property: Property,1385 ) -> DispatchResult {1386 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1387 properties.try_scoped_set(scope, property.key, property.value)1388 })1389 .map_err(<Error<T>>::from)?;13901391 Ok(())1392 }13931394 1395 1396 1397 1398 1399 1400 pub fn set_scoped_collection_properties(1401 collection_id: CollectionId,1402 scope: PropertyScope,1403 properties: impl Iterator<Item = Property>,1404 ) -> DispatchResult {1405 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1406 stored_properties.try_scoped_set_from_iter(scope, properties)1407 })1408 .map_err(<Error<T>>::from)?;14091410 Ok(())1411 }14121413 1414 1415 1416 1417 1418 pub fn set_collection_properties(1419 collection: &CollectionHandle<T>,1420 sender: &T::CrossAccountId,1421 properties: impl Iterator<Item = Property>,1422 ) -> DispatchResult {1423 Self::modify_collection_properties(1424 collection,1425 sender,1426 properties.map(|property| (property.key, Some(property.value))),1427 )1428 }14291430 1431 1432 1433 1434 1435 pub fn delete_collection_property(1436 collection: &CollectionHandle<T>,1437 sender: &T::CrossAccountId,1438 property_key: PropertyKey,1439 ) -> DispatchResult {1440 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1441 }14421443 1444 1445 1446 1447 1448 pub fn delete_collection_properties(1449 collection: &CollectionHandle<T>,1450 sender: &T::CrossAccountId,1451 property_keys: impl Iterator<Item = PropertyKey>,1452 ) -> DispatchResult {1453 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1454 }14551456 1457 1458 1459 1460 1461 1462 pub fn set_property_permission_unchecked(1463 collection: CollectionId,1464 property_permission: PropertyKeyPermission,1465 ) -> DispatchResult {1466 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1467 permissions.try_set(property_permission.key, property_permission.permission)1468 })1469 .map_err(<Error<T>>::from)?;1470 Ok(())1471 }14721473 1474 1475 1476 1477 1478 pub fn set_property_permission(1479 collection: &CollectionHandle<T>,1480 sender: &T::CrossAccountId,1481 property_permission: PropertyKeyPermission,1482 ) -> DispatchResult {1483 Self::set_scoped_property_permission(1484 collection,1485 sender,1486 PropertyScope::None,1487 property_permission,1488 )1489 }14901491 1492 1493 1494 1495 1496 1497 pub fn set_scoped_property_permission(1498 collection: &CollectionHandle<T>,1499 sender: &T::CrossAccountId,1500 scope: PropertyScope,1501 property_permission: PropertyKeyPermission,1502 ) -> DispatchResult {1503 collection.check_is_owner_or_admin(sender)?;15041505 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1506 let current_permission = all_permissions.get(&property_permission.key);1507 if matches![1508 current_permission,1509 Some(PropertyPermission { mutable: false, .. })1510 ] {1511 return Err(<Error<T>>::NoPermission.into());1512 }15131514 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1515 let property_permission = property_permission.clone();1516 permissions.try_scoped_set(1517 scope,1518 property_permission.key,1519 property_permission.permission,1520 )1521 })1522 .map_err(<Error<T>>::from)?;15231524 Self::deposit_event(Event::PropertyPermissionSet(1525 collection.id,1526 property_permission.key,1527 ));1528 <PalletEvm<T>>::deposit_log(1529 erc::CollectionHelpersEvents::CollectionChanged {1530 collection_id: eth::collection_id_to_address(collection.id),1531 }1532 .to_log(T::ContractAddress::get()),1533 );15341535 Ok(())1536 }15371538 1539 1540 1541 1542 1543 #[transactional]1544 pub fn set_token_property_permissions(1545 collection: &CollectionHandle<T>,1546 sender: &T::CrossAccountId,1547 property_permissions: Vec<PropertyKeyPermission>,1548 ) -> DispatchResult {1549 Self::set_scoped_token_property_permissions(1550 collection,1551 sender,1552 PropertyScope::None,1553 property_permissions,1554 )1555 }15561557 1558 1559 1560 1561 1562 1563 #[transactional]1564 pub fn set_scoped_token_property_permissions(1565 collection: &CollectionHandle<T>,1566 sender: &T::CrossAccountId,1567 scope: PropertyScope,1568 property_permissions: Vec<PropertyKeyPermission>,1569 ) -> DispatchResult {1570 for prop_pemission in property_permissions {1571 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1572 }15731574 Ok(())1575 }15761577 1578 pub fn get_collection_property(1579 collection_id: CollectionId,1580 key: &PropertyKey,1581 ) -> Option<PropertyValue> {1582 Self::collection_properties(collection_id).get(key).cloned()1583 }15841585 1586 pub fn bytes_keys_to_property_keys(1587 keys: Vec<Vec<u8>>,1588 ) -> Result<Vec<PropertyKey>, DispatchError> {1589 keys.into_iter()1590 .map(|key| -> Result<PropertyKey, DispatchError> {1591 key.try_into()1592 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1593 })1594 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1595 }15961597 1598 pub fn filter_collection_properties(1599 collection_id: CollectionId,1600 keys: Option<Vec<PropertyKey>>,1601 ) -> Result<Vec<Property>, DispatchError> {1602 let properties = Self::collection_properties(collection_id);16031604 let properties = keys1605 .map(|keys| {1606 keys.into_iter()1607 .filter_map(|key| {1608 properties.get(&key).map(|value| Property {1609 key,1610 value: value.clone(),1611 })1612 })1613 .collect()1614 })1615 .unwrap_or_else(|| {1616 properties1617 .into_iter()1618 .map(|(key, value)| Property { key, value })1619 .collect()1620 });16211622 Ok(properties)1623 }16241625 1626 pub fn filter_property_permissions(1627 collection_id: CollectionId,1628 keys: Option<Vec<PropertyKey>>,1629 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1630 let permissions = Self::property_permissions(collection_id);16311632 let key_permissions = keys1633 .map(|keys| {1634 keys.into_iter()1635 .filter_map(|key| {1636 permissions1637 .get(&key)1638 .map(|permission| PropertyKeyPermission {1639 key,1640 permission: permission.clone(),1641 })1642 })1643 .collect()1644 })1645 .unwrap_or_else(|| {1646 permissions1647 .into_iter()1648 .map(|(key, permission)| PropertyKeyPermission { key, permission })1649 .collect()1650 });16511652 Ok(key_permissions)1653 }16541655 1656 1657 1658 pub fn toggle_allowlist(1659 collection: &CollectionHandle<T>,1660 sender: &T::CrossAccountId,1661 user: &T::CrossAccountId,1662 allowed: bool,1663 ) -> DispatchResult {1664 collection.check_is_owner_or_admin(sender)?;16651666 16671668 if allowed {1669 <Allowlist<T>>::insert((collection.id, user), true);1670 Self::deposit_event(Event::<T>::AllowListAddressAdded(1671 collection.id,1672 user.clone(),1673 ));1674 } else {1675 <Allowlist<T>>::remove((collection.id, user));1676 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1677 collection.id,1678 user.clone(),1679 ));1680 }16811682 <PalletEvm<T>>::deposit_log(1683 erc::CollectionHelpersEvents::CollectionChanged {1684 collection_id: eth::collection_id_to_address(collection.id),1685 }1686 .to_log(T::ContractAddress::get()),1687 );16881689 Ok(())1690 }16911692 1693 1694 1695 pub fn toggle_admin(1696 collection: &CollectionHandle<T>,1697 sender: &T::CrossAccountId,1698 user: &T::CrossAccountId,1699 admin: bool,1700 ) -> DispatchResult {1701 collection.check_is_internal()?;1702 collection.check_is_owner(sender)?;17031704 let is_admin = <IsAdmin<T>>::get((collection.id, user));1705 if is_admin == admin {1706 if admin {1707 return Ok(());1708 } else {1709 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1710 }1711 }1712 let amount = <AdminAmount<T>>::get(collection.id);17131714 17151716 if admin {1717 let amount = amount1718 .checked_add(1)1719 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1720 ensure!(1721 amount <= Self::collection_admins_limit(),1722 <Error<T>>::CollectionAdminCountExceeded,1723 );17241725 <AdminAmount<T>>::insert(collection.id, amount);1726 <IsAdmin<T>>::insert((collection.id, user), true);17271728 Self::deposit_event(Event::<T>::CollectionAdminAdded(1729 collection.id,1730 user.clone(),1731 ));1732 } else {1733 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1734 <IsAdmin<T>>::remove((collection.id, user));17351736 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1737 collection.id,1738 user.clone(),1739 ));1740 }17411742 <PalletEvm<T>>::deposit_log(1743 erc::CollectionHelpersEvents::CollectionChanged {1744 collection_id: eth::collection_id_to_address(collection.id),1745 }1746 .to_log(T::ContractAddress::get()),1747 );17481749 Ok(())1750 }17511752 1753 pub fn update_limits(1754 user: &T::CrossAccountId,1755 collection: &mut CollectionHandle<T>,1756 new_limit: CollectionLimits,1757 ) -> DispatchResult {1758 collection.check_is_internal()?;1759 collection.check_is_owner_or_admin(user)?;17601761 collection.limits =1762 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;17631764 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1765 <PalletEvm<T>>::deposit_log(1766 erc::CollectionHelpersEvents::CollectionChanged {1767 collection_id: eth::collection_id_to_address(collection.id),1768 }1769 .to_log(T::ContractAddress::get()),1770 );17711772 collection.save()1773 }17741775 1776 fn clamp_limits(1777 mode: CollectionMode,1778 old_limit: &CollectionLimits,1779 mut new_limit: CollectionLimits,1780 ) -> Result<CollectionLimits, DispatchError> {1781 let limits = old_limit;1782 limit_default!(old_limit, new_limit,1783 account_token_ownership_limit => ensure!(1784 new_limit <= MAX_TOKEN_OWNERSHIP,1785 <Error<T>>::CollectionLimitBoundsExceeded,1786 ),1787 sponsored_data_size => ensure!(1788 new_limit <= CUSTOM_DATA_LIMIT,1789 <Error<T>>::CollectionLimitBoundsExceeded,1790 ),17911792 sponsored_data_rate_limit => {},1793 token_limit => ensure!(1794 old_limit >= new_limit && new_limit > 0,1795 <Error<T>>::CollectionTokenLimitExceeded1796 ),17971798 sponsor_transfer_timeout(match mode {1799 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1800 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1801 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1802 }) => ensure!(1803 new_limit <= MAX_SPONSOR_TIMEOUT,1804 <Error<T>>::CollectionLimitBoundsExceeded,1805 ),1806 sponsor_approve_timeout => {},1807 owner_can_transfer => ensure!(1808 !limits.owner_can_transfer_instaled() ||1809 old_limit || !new_limit,1810 <Error<T>>::OwnerPermissionsCantBeReverted,1811 ),1812 owner_can_destroy => ensure!(1813 old_limit || !new_limit,1814 <Error<T>>::OwnerPermissionsCantBeReverted,1815 ),1816 transfers_enabled => {},1817 );1818 Ok(new_limit)1819 }18201821 1822 pub fn update_permissions(1823 user: &T::CrossAccountId,1824 collection: &mut CollectionHandle<T>,1825 new_permission: CollectionPermissions,1826 ) -> DispatchResult {1827 collection.check_is_internal()?;1828 collection.check_is_owner_or_admin(user)?;1829 collection.permissions = Self::clamp_permissions(1830 collection.mode.clone(),1831 &collection.permissions,1832 new_permission,1833 )?;18341835 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1836 <PalletEvm<T>>::deposit_log(1837 erc::CollectionHelpersEvents::CollectionChanged {1838 collection_id: eth::collection_id_to_address(collection.id),1839 }1840 .to_log(T::ContractAddress::get()),1841 );18421843 collection.save()1844 }18451846 1847 fn clamp_permissions(1848 _mode: CollectionMode,1849 old_permission: &CollectionPermissions,1850 mut new_permission: CollectionPermissions,1851 ) -> Result<CollectionPermissions, DispatchError> {1852 limit_default_clone!(old_permission, new_permission,1853 access => {},1854 mint_mode => {},1855 nesting => { },1856 );1857 Ok(new_permission)1858 }18591860 1861 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1862 CollectionProperties::<T>::mutate(collection_id, |properties| {1863 properties.recompute_consumed_space();1864 });18651866 Ok(())1867 }1868}186918701871#[macro_export]1872macro_rules! unsupported {1873 ($runtime:path) => {1874 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1875 };1876}187718781879pub trait CommonWeightInfo<CrossAccountId> {1880 1881 fn create_item(data: &CreateItemData) -> Weight {1882 Self::create_multiple_items(from_ref(data))1883 }18841885 1886 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;18871888 1889 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;18901891 1892 fn burn_item() -> Weight;18931894 1895 1896 1897 fn set_collection_properties(amount: u32) -> Weight;18981899 1900 1901 1902 fn delete_collection_properties(amount: u32) -> Weight;19031904 1905 1906 1907 fn set_token_properties(amount: u32) -> Weight;19081909 1910 1911 1912 fn delete_token_properties(amount: u32) -> Weight;19131914 1915 1916 1917 fn set_token_property_permissions(amount: u32) -> Weight;19181919 1920 fn transfer() -> Weight;19211922 1923 fn approve() -> Weight;19241925 1926 fn approve_from() -> Weight;19271928 1929 fn transfer_from() -> Weight;19301931 1932 fn burn_from() -> Weight;19331934 1935 1936 1937 1938 fn burn_recursively_self_raw() -> Weight;19391940 1941 1942 1943 fn burn_recursively_breadth_raw(amount: u32) -> Weight;19441945 1946 1947 1948 1949 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1950 Self::burn_recursively_self_raw()1951 .saturating_mul(max_selfs.max(1) as u64)1952 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1953 }19541955 1956 fn token_owner() -> Weight;19571958 1959 fn set_allowance_for_all() -> Weight;19601961 1962 fn force_repair_item() -> Weight;1963}196419651966pub trait RefungibleExtensionsWeightInfo {1967 1968 fn repartition() -> Weight;1969}197019711972197319741975pub trait CommonCollectionOperations<T: Config> {1976 1977 1978 1979 1980 1981 1982 fn create_item(1983 &self,1984 sender: T::CrossAccountId,1985 to: T::CrossAccountId,1986 data: CreateItemData,1987 nesting_budget: &dyn Budget,1988 ) -> DispatchResultWithPostInfo;19891990 1991 1992 1993 1994 1995 1996 fn create_multiple_items(1997 &self,1998 sender: T::CrossAccountId,1999 to: T::CrossAccountId,2000 data: Vec<CreateItemData>,2001 nesting_budget: &dyn Budget,2002 ) -> DispatchResultWithPostInfo;20032004 2005 2006 2007 2008 2009 2010 fn create_multiple_items_ex(2011 &self,2012 sender: T::CrossAccountId,2013 data: CreateItemExData<T::CrossAccountId>,2014 nesting_budget: &dyn Budget,2015 ) -> DispatchResultWithPostInfo;20162017 2018 2019 2020 2021 2022 fn burn_item(2023 &self,2024 sender: T::CrossAccountId,2025 token: TokenId,2026 amount: u128,2027 ) -> DispatchResultWithPostInfo;20282029 2030 2031 2032 2033 2034 2035 fn burn_item_recursively(2036 &self,2037 sender: T::CrossAccountId,2038 token: TokenId,2039 self_budget: &dyn Budget,2040 breadth_budget: &dyn Budget,2041 ) -> DispatchResultWithPostInfo;20422043 2044 2045 2046 2047 fn set_collection_properties(2048 &self,2049 sender: T::CrossAccountId,2050 properties: Vec<Property>,2051 ) -> DispatchResultWithPostInfo;20522053 2054 2055 2056 2057 fn delete_collection_properties(2058 &self,2059 sender: &T::CrossAccountId,2060 property_keys: Vec<PropertyKey>,2061 ) -> DispatchResultWithPostInfo;20622063 2064 2065 2066 2067 2068 2069 2070 2071 2072 fn set_token_properties(2073 &self,2074 sender: T::CrossAccountId,2075 token_id: TokenId,2076 properties: Vec<Property>,2077 budget: &dyn Budget,2078 ) -> DispatchResultWithPostInfo;20792080 2081 2082 2083 2084 2085 2086 2087 2088 2089 fn delete_token_properties(2090 &self,2091 sender: T::CrossAccountId,2092 token_id: TokenId,2093 property_keys: Vec<PropertyKey>,2094 budget: &dyn Budget,2095 ) -> DispatchResultWithPostInfo;20962097 2098 2099 2100 2101 2102 2103 fn set_token_property_permissions(2104 &self,2105 sender: &T::CrossAccountId,2106 property_permissions: Vec<PropertyKeyPermission>,2107 ) -> DispatchResultWithPostInfo;21082109 2110 2111 2112 2113 2114 2115 2116 fn transfer(2117 &self,2118 sender: T::CrossAccountId,2119 to: T::CrossAccountId,2120 token: TokenId,2121 amount: u128,2122 budget: &dyn Budget,2123 ) -> DispatchResultWithPostInfo;21242125 2126 2127 2128 2129 2130 2131 fn approve(2132 &self,2133 sender: T::CrossAccountId,2134 spender: T::CrossAccountId,2135 token: TokenId,2136 amount: u128,2137 ) -> DispatchResultWithPostInfo;21382139 2140 2141 2142 2143 2144 2145 2146 fn approve_from(2147 &self,2148 sender: T::CrossAccountId,2149 from: T::CrossAccountId,2150 to: T::CrossAccountId,2151 token: TokenId,2152 amount: u128,2153 ) -> DispatchResultWithPostInfo;21542155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 fn transfer_from(2166 &self,2167 sender: T::CrossAccountId,2168 from: T::CrossAccountId,2169 to: T::CrossAccountId,2170 token: TokenId,2171 amount: u128,2172 budget: &dyn Budget,2173 ) -> DispatchResultWithPostInfo;21742175 2176 2177 2178 2179 2180 2181 2182 2183 2184 fn burn_from(2185 &self,2186 sender: T::CrossAccountId,2187 from: T::CrossAccountId,2188 token: TokenId,2189 amount: u128,2190 budget: &dyn Budget,2191 ) -> DispatchResultWithPostInfo;21922193 2194 2195 2196 2197 2198 2199 fn check_nesting(2200 &self,2201 sender: T::CrossAccountId,2202 from: (CollectionId, TokenId),2203 under: TokenId,2204 budget: &dyn Budget,2205 ) -> DispatchResult;22062207 2208 2209 2210 2211 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22122213 2214 2215 2216 2217 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22182219 2220 2221 2222 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;22232224 2225 fn collection_tokens(&self) -> Vec<TokenId>;22262227 2228 2229 2230 fn token_exists(&self, token: TokenId) -> bool;22312232 2233 fn last_token_id(&self) -> TokenId;22342235 2236 2237 2238 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22392240 2241 2242 2243 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22442245 2246 2247 2248 2249 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22502251 2252 2253 2254 2255 2256 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22572258 2259 fn total_supply(&self) -> u32;22602261 2262 2263 2264 fn account_balance(&self, account: T::CrossAccountId) -> u32;22652266 2267 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;22682269 2270 fn total_pieces(&self, token: TokenId) -> Option<u128>;22712272 2273 2274 2275 2276 2277 fn allowance(2278 &self,2279 sender: T::CrossAccountId,2280 spender: T::CrossAccountId,2281 token: TokenId,2282 ) -> u128;22832284 2285 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;22862287 2288 2289 2290 2291 fn set_allowance_for_all(2292 &self,2293 owner: T::CrossAccountId,2294 operator: T::CrossAccountId,2295 approve: bool,2296 ) -> DispatchResultWithPostInfo;22972298 2299 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;23002301 2302 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2303}230423052306pub trait RefungibleExtensions<T>2307where2308 T: Config,2309{2310 2311 2312 2313 2314 2315 2316 2317 fn repartition(2318 &self,2319 sender: &T::CrossAccountId,2320 token: TokenId,2321 amount: u128,2322 ) -> DispatchResultWithPostInfo;2323}23242325232623272328pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2329 let post_info = PostDispatchInfo {2330 actual_weight: Some(weight),2331 pays_fee: Pays::Yes,2332 };2333 match res {2334 Ok(()) => Ok(post_info),2335 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2336 }2337}23382339impl<T: Config> From<PropertiesError> for Error<T> {2340 fn from(error: PropertiesError) -> Self {2341 match error {2342 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2343 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2344 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2345 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2346 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2347 }2348 }2349}