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,74 COLLECTION_NUMBER_LIMIT,75 Collection,76 RpcCollection,77 CollectionFlags,78 RpcCollectionFlags,79 CollectionId,80 CreateItemData,81 MAX_TOKEN_PREFIX_LENGTH,82 COLLECTION_ADMINS_LIMIT,83 TokenId,84 TokenChild,85 CollectionStats,86 MAX_TOKEN_OWNERSHIP,87 CollectionMode,88 NFT_SPONSOR_TRANSFER_TIMEOUT,89 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,90 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,91 MAX_SPONSOR_TIMEOUT,92 CUSTOM_DATA_LIMIT,93 CollectionLimits,94 CreateCollectionData,95 SponsorshipState,96 CreateItemExData,97 SponsoringRateLimit,98 budget::Budget,99 PhantomType,100 Property,101 Properties,102 PropertiesPermissionMap,103 PropertyKey,104 PropertyValue,105 PropertyPermission,106 PropertiesError,107 TokenOwnerError,108 PropertyKeyPermission,109 TokenData,110 TrySetProperty,111 PropertyScope,112 113 RmrkCollectionInfo,114 RmrkInstanceInfo,115 RmrkResourceInfo,116 RmrkPropertyInfo,117 RmrkBaseInfo,118 RmrkPartType,119 RmrkBoundedTheme,120 RmrkNftChild,121 CollectionPermissions,122};123use up_pov_estimate_rpc::PovInfo;124125pub use pallet::*;126use sp_core::H160;127use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};128129use crate::erc::CollectionHelpersEvents;130#[cfg(feature = "runtime-benchmarks")]131pub mod benchmarking;132pub mod dispatch;133pub mod erc;134pub mod eth;135pub mod weights;136137138pub type SelfWeightOf<T> = <T as Config>::WeightInfo;139140141142143144145146#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]147pub struct CollectionHandle<T: Config> {148 149 pub id: CollectionId,150 collection: Collection<T::AccountId>,151 152 pub recorder: SubstrateRecorder<T>,153}154155impl<T: Config> WithRecorder<T> for CollectionHandle<T> {156 fn recorder(&self) -> &SubstrateRecorder<T> {157 &self.recorder158 }159 fn into_recorder(self) -> SubstrateRecorder<T> {160 self.recorder161 }162}163164impl<T: Config> CollectionHandle<T> {165 166 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {167 <CollectionById<T>>::get(id).map(|collection| Self {168 id,169 collection,170 recorder: SubstrateRecorder::new(gas_limit),171 })172 }173174 175 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {176 <CollectionById<T>>::get(id).map(|collection| Self {177 id,178 collection,179 recorder,180 })181 }182183 184 185 pub fn new(id: CollectionId) -> Option<Self> {186 Self::new_with_gas_limit(id, u64::MAX)187 }188189 190 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {191 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)192 }193194 195 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {196 self.recorder197 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(198 <T as frame_system::Config>::DbWeight::get()199 .read200 .saturating_mul(reads),201 )))202 }203204 205 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {206 self.recorder207 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(208 <T as frame_system::Config>::DbWeight::get()209 .write210 .saturating_mul(writes),211 )))212 }213214 215 pub fn consume_store_reads_and_writes(216 &self,217 reads: u64,218 writes: u64,219 ) -> evm_coder::execution::Result<()> {220 let weight = <T as frame_system::Config>::DbWeight::get();221 let reads = weight.read.saturating_mul(reads);222 let writes = weight.read.saturating_mul(writes);223 self.recorder224 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(225 reads.saturating_add(writes),226 )))227 }228229 230 pub fn save(&self) -> DispatchResult {231 <CollectionById<T>>::insert(self.id, &self.collection);232 Ok(())233 }234235 236 237 238 239 240 pub fn set_sponsor(241 &mut self,242 sender: &T::CrossAccountId,243 sponsor: T::AccountId,244 ) -> DispatchResult {245 self.check_is_internal()?;246 self.check_is_owner_or_admin(sender)?;247248 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());249250 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));251 <PalletEvm<T>>::deposit_log(252 erc::CollectionHelpersEvents::CollectionChanged {253 collection_id: eth::collection_id_to_address(self.id),254 }255 .to_log(T::ContractAddress::get()),256 );257258 self.save()259 }260261 262 263 264 265 266 267 268 269 270 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {271 self.check_is_internal()?;272273 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());274275 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));276 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));277 <PalletEvm<T>>::deposit_log(278 erc::CollectionHelpersEvents::CollectionChanged {279 collection_id: eth::collection_id_to_address(self.id),280 }281 .to_log(T::ContractAddress::get()),282 );283284 self.save()285 }286287 288 289 290 291 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {292 self.check_is_internal()?;293 ensure!(294 self.collection.sponsorship.pending_sponsor() == Some(sender),295 Error::<T>::ConfirmSponsorshipFail296 );297298 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());299300 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));301 <PalletEvm<T>>::deposit_log(302 erc::CollectionHelpersEvents::CollectionChanged {303 collection_id: eth::collection_id_to_address(self.id),304 }305 .to_log(T::ContractAddress::get()),306 );307308 self.save()309 }310311 312 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {313 self.check_is_internal()?;314 self.check_is_owner_or_admin(sender)?;315316 self.collection.sponsorship = SponsorshipState::Disabled;317318 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));319 <PalletEvm<T>>::deposit_log(320 erc::CollectionHelpersEvents::CollectionChanged {321 collection_id: eth::collection_id_to_address(self.id),322 }323 .to_log(T::ContractAddress::get()),324 );325 self.save()326 }327328 329 330 331 332 pub fn force_remove_sponsor(&mut self) -> DispatchResult {333 self.check_is_internal()?;334335 self.collection.sponsorship = SponsorshipState::Disabled;336337 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));338 <PalletEvm<T>>::deposit_log(339 erc::CollectionHelpersEvents::CollectionChanged {340 collection_id: eth::collection_id_to_address(self.id),341 }342 .to_log(T::ContractAddress::get()),343 );344 self.save()345 }346347 348 349 pub fn check_is_internal(&self) -> DispatchResult {350 if self.flags.external {351 return Err(<Error<T>>::CollectionIsExternal)?;352 }353354 Ok(())355 }356357 358 359 pub fn check_is_external(&self) -> DispatchResult {360 if !self.flags.external {361 return Err(<Error<T>>::CollectionIsInternal)?;362 }363364 Ok(())365 }366}367368impl<T: Config> Deref for CollectionHandle<T> {369 type Target = Collection<T::AccountId>;370371 fn deref(&self) -> &Self::Target {372 &self.collection373 }374}375376impl<T: Config> DerefMut for CollectionHandle<T> {377 fn deref_mut(&mut self) -> &mut Self::Target {378 &mut self.collection379 }380}381382impl<T: Config> CollectionHandle<T> {383 384 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {385 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);386 Ok(())387 }388389 390 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {391 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))392 }393394 395 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {396 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);397 Ok(())398 }399400 401 402 403 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {404 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)405 }406407 408 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {409 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)410 }411412 413 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {414 ensure!(415 <Allowlist<T>>::get((self.id, user)),416 <Error<T>>::AddressNotInAllowlist417 );418 Ok(())419 }420421 422 423 424 pub fn change_owner(425 &mut self,426 caller: T::CrossAccountId,427 new_owner: T::CrossAccountId,428 ) -> DispatchResult {429 self.check_is_internal()?;430 self.check_is_owner(&caller)?;431 self.collection.owner = new_owner.as_sub().clone();432433 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(434 self.id,435 new_owner.as_sub().clone(),436 ));437 <PalletEvm<T>>::deposit_log(438 erc::CollectionHelpersEvents::CollectionChanged {439 collection_id: eth::collection_id_to_address(self.id),440 }441 .to_log(T::ContractAddress::get()),442 );443444 self.save()445 }446}447448#[frame_support::pallet]449pub mod pallet {450 use super::*;451 use dispatch::CollectionDispatch;452 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};453 use frame_system::pallet_prelude::*;454 use frame_support::traits::Currency;455 use up_data_structs::{TokenId, mapping::TokenAddressMapping};456 use scale_info::TypeInfo;457 use weights::WeightInfo;458459 #[pallet::config]460 pub trait Config:461 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo462 {463 464 type WeightInfo: WeightInfo;465466 467 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;468469 470 type Currency: Currency<Self::AccountId>;471472 473 #[pallet::constant]474 type CollectionCreationPrice: Get<475 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,476 >;477478 479 type CollectionDispatch: CollectionDispatch<Self>;480481 482 type TreasuryAccountId: Get<Self::AccountId>;483484 485 #[pallet::constant]486 type ContractAddress: Get<H160>;487488 489 type EvmTokenAddressMapping: TokenAddressMapping<H160>;490491 492 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;493 }494495 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);496497 #[pallet::pallet]498 #[pallet::storage_version(STORAGE_VERSION)]499 #[pallet::generate_store(pub(super) trait Store)]500 pub struct Pallet<T>(_);501502 #[pallet::extra_constants]503 impl<T: Config> Pallet<T> {504 505 pub fn collection_admins_limit() -> u32 {506 COLLECTION_ADMINS_LIMIT507 }508 }509510 impl<T: Config> Pallet<T> {511 512 pub fn deposit_event(event: Event<T>) {513 let event = <T as Config>::RuntimeEvent::from(event);514 let event = event.into();515 <frame_system::Pallet<T>>::deposit_event(event)516 }517 }518519 #[pallet::event]520 pub enum Event<T: Config> {521 522 CollectionCreated(523 524 CollectionId,525 526 u8,527 528 T::AccountId,529 ),530531 532 CollectionDestroyed(533 534 CollectionId,535 ),536537 538 ItemCreated(539 540 CollectionId,541 542 TokenId,543 544 T::CrossAccountId,545 546 u128,547 ),548549 550 ItemDestroyed(551 552 CollectionId,553 554 TokenId,555 556 T::CrossAccountId,557 558 u128,559 ),560561 562 Transfer(563 564 CollectionId,565 566 TokenId,567 568 T::CrossAccountId,569 570 T::CrossAccountId,571 572 u128,573 ),574575 576 Approved(577 578 CollectionId,579 580 TokenId,581 582 T::CrossAccountId,583 584 T::CrossAccountId,585 586 u128,587 ),588589 590 ApprovedForAll(591 592 CollectionId,593 594 T::CrossAccountId,595 596 T::CrossAccountId,597 598 bool,599 ),600601 602 CollectionPropertySet(603 604 CollectionId,605 606 PropertyKey,607 ),608609 610 CollectionPropertyDeleted(611 612 CollectionId,613 614 PropertyKey,615 ),616617 618 TokenPropertySet(619 620 CollectionId,621 622 TokenId,623 624 PropertyKey,625 ),626627 628 TokenPropertyDeleted(629 630 CollectionId,631 632 TokenId,633 634 PropertyKey,635 ),636637 638 PropertyPermissionSet(639 640 CollectionId,641 642 PropertyKey,643 ),644645 646 AllowListAddressAdded(647 648 CollectionId,649 650 T::CrossAccountId,651 ),652653 654 AllowListAddressRemoved(655 656 CollectionId,657 658 T::CrossAccountId,659 ),660661 662 CollectionAdminAdded(663 664 CollectionId,665 666 T::CrossAccountId,667 ),668669 670 CollectionAdminRemoved(671 672 CollectionId,673 674 T::CrossAccountId,675 ),676677 678 CollectionLimitSet(679 680 CollectionId,681 ),682683 684 CollectionOwnerChanged(685 686 CollectionId,687 688 T::AccountId,689 ),690691 692 CollectionPermissionSet(693 694 CollectionId,695 ),696697 698 CollectionSponsorSet(699 700 CollectionId,701 702 T::AccountId,703 ),704705 706 SponsorshipConfirmed(707 708 CollectionId,709 710 T::AccountId,711 ),712713 714 CollectionSponsorRemoved(715 716 CollectionId,717 ),718 }719720 #[pallet::error]721 pub enum Error<T> {722 723 CollectionNotFound,724 725 MustBeTokenOwner,726 727 NoPermission,728 729 CantDestroyNotEmptyCollection,730 731 PublicMintingNotAllowed,732 733 AddressNotInAllowlist,734735 736 CollectionNameLimitExceeded,737 738 CollectionDescriptionLimitExceeded,739 740 CollectionTokenPrefixLimitExceeded,741 742 TotalCollectionsLimitExceeded,743 744 CollectionAdminCountExceeded,745 746 CollectionLimitBoundsExceeded,747 748 OwnerPermissionsCantBeReverted,749 750 TransferNotAllowed,751 752 AccountTokenLimitExceeded,753 754 CollectionTokenLimitExceeded,755 756 MetadataFlagFrozen,757758 759 TokenNotFound,760 761 TokenValueTooLow,762 763 ApprovedValueTooLow,764 765 CantApproveMoreThanOwned,766 767 AddressIsNotEthMirror,768769 770 AddressIsZero,771772 773 UnsupportedOperation,774775 776 NotSufficientFounds,777778 779 UserIsNotAllowedToNest,780 781 SourceCollectionIsNotAllowedToNest,782783 784 CollectionFieldSizeExceeded,785786 787 NoSpaceForProperty,788789 790 PropertyLimitReached,791792 793 PropertyKeyIsTooLong,794795 796 InvalidCharacterInPropertyKey,797798 799 EmptyPropertyKey,800801 802 CollectionIsExternal,803804 805 CollectionIsInternal,806807 808 ConfirmSponsorshipFail,809810 811 UserIsNotCollectionAdmin,812 }813814 815 #[pallet::storage]816 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;817818 819 #[pallet::storage]820 pub type DestroyedCollectionCount<T> =821 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;822823 824 #[pallet::storage]825 pub type CollectionById<T> = StorageMap<826 Hasher = Blake2_128Concat,827 Key = CollectionId,828 Value = Collection<<T as frame_system::Config>::AccountId>,829 QueryKind = OptionQuery,830 >;831832 833 #[pallet::storage]834 #[pallet::getter(fn collection_properties)]835 pub type CollectionProperties<T> = StorageMap<836 Hasher = Blake2_128Concat,837 Key = CollectionId,838 Value = Properties,839 QueryKind = ValueQuery,840 OnEmpty = up_data_structs::CollectionProperties,841 >;842843 844 #[pallet::storage]845 #[pallet::getter(fn property_permissions)]846 pub type CollectionPropertyPermissions<T> = StorageMap<847 Hasher = Blake2_128Concat,848 Key = CollectionId,849 Value = PropertiesPermissionMap,850 QueryKind = ValueQuery,851 >;852853 854 #[pallet::storage]855 pub type AdminAmount<T> = StorageMap<856 Hasher = Blake2_128Concat,857 Key = CollectionId,858 Value = u32,859 QueryKind = ValueQuery,860 >;861862 863 #[pallet::storage]864 pub type IsAdmin<T: Config> = StorageNMap<865 Key = (866 Key<Blake2_128Concat, CollectionId>,867 Key<Blake2_128Concat, T::CrossAccountId>,868 ),869 Value = bool,870 QueryKind = ValueQuery,871 >;872873 874 #[pallet::storage]875 pub type Allowlist<T: Config> = StorageNMap<876 Key = (877 Key<Blake2_128Concat, CollectionId>,878 Key<Blake2_128Concat, T::CrossAccountId>,879 ),880 Value = bool,881 QueryKind = ValueQuery,882 >;883884 885 #[pallet::storage]886 pub type DummyStorageValue<T: Config> = StorageValue<887 Value = (888 CollectionStats,889 CollectionId,890 TokenId,891 TokenChild,892 PhantomType<(893 TokenData<T::CrossAccountId>,894 RpcCollection<T::AccountId>,895 896 RmrkCollectionInfo<T::AccountId>,897 RmrkInstanceInfo<T::AccountId>,898 RmrkResourceInfo,899 RmrkPropertyInfo,900 RmrkBaseInfo<T::AccountId>,901 RmrkPartType,902 RmrkBoundedTheme,903 RmrkNftChild,904 905 PovInfo,906 )>,907 ),908 QueryKind = OptionQuery,909 >;910911 #[pallet::hooks]912 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {913 fn on_runtime_upgrade() -> Weight {914 StorageVersion::new(1).put::<Pallet<T>>();915916 Weight::zero()917 }918 }919}920921impl<T: Config> Pallet<T> {922 923 924 925 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {926 ensure!(927 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,928 <Error<T>>::AddressIsZero929 );930 Ok(())931 }932933 934 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {935 <IsAdmin<T>>::iter_prefix((collection,))936 .map(|(a, _)| a)937 .collect()938 }939940 941 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {942 <Allowlist<T>>::iter_prefix((collection,))943 .map(|(a, _)| a)944 .collect()945 }946947 948 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {949 <Allowlist<T>>::get((collection, user))950 }951952 953 pub fn collection_stats() -> CollectionStats {954 let created = <CreatedCollectionCount<T>>::get();955 let destroyed = <DestroyedCollectionCount<T>>::get();956 CollectionStats {957 created: created.0,958 destroyed: destroyed.0,959 alive: created.0 - destroyed.0,960 }961 }962963 964 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {965 let collection = <CollectionById<T>>::get(collection)?;966 let limits = collection.limits;967 let effective_limits = CollectionLimits {968 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),969 sponsored_data_size: Some(limits.sponsored_data_size()),970 sponsored_data_rate_limit: Some(971 limits972 .sponsored_data_rate_limit973 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),974 ),975 token_limit: Some(limits.token_limit()),976 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(977 match collection.mode {978 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,979 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,980 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,981 },982 )),983 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),984 owner_can_transfer: Some(limits.owner_can_transfer()),985 owner_can_destroy: Some(limits.owner_can_destroy()),986 transfers_enabled: Some(limits.transfers_enabled()),987 };988989 Some(effective_limits)990 }991992 993 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {994 let Collection {995 name,996 description,997 owner,998 mode,999 token_prefix,1000 sponsorship,1001 limits,1002 permissions,1003 flags,1004 } = <CollectionById<T>>::get(collection)?;10051006 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)1007 .into_iter()1008 .map(|(key, permission)| PropertyKeyPermission { key, permission })1009 .collect();10101011 let properties = <CollectionProperties<T>>::get(collection)1012 .into_iter()1013 .map(|(key, value)| Property { key, value })1014 .collect();10151016 let permissions = CollectionPermissions {1017 access: Some(permissions.access()),1018 mint_mode: Some(permissions.mint_mode()),1019 nesting: Some(permissions.nesting().clone()),1020 };10211022 Some(RpcCollection {1023 name: name.into_inner(),1024 description: description.into_inner(),1025 owner,1026 mode,1027 token_prefix: token_prefix.into_inner(),1028 sponsorship,1029 limits,1030 permissions,1031 token_property_permissions,1032 properties,1033 read_only: flags.external,10341035 flags: RpcCollectionFlags {1036 foreign: flags.foreign,1037 erc721metadata: flags.erc721metadata,1038 },1039 })1040 }1041}10421043macro_rules! limit_default {1044 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1045 $(1046 if let Some($new) = $new.$field {1047 let $old = $old.$field($($arg)?);1048 let _ = $new;1049 let _ = $old;1050 $check1051 } else {1052 $new.$field = $old.$field1053 }1054 )*1055 }};1056}1057macro_rules! limit_default_clone {1058 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1059 $(1060 if let Some($new) = $new.$field.clone() {1061 let $old = $old.$field($($arg)?);1062 let _ = $new;1063 let _ = $old;1064 $check1065 } else {1066 $new.$field = $old.$field.clone()1067 }1068 )*1069 }};1070}10711072impl<T: Config> Pallet<T> {1073 1074 1075 1076 1077 1078 pub fn init_collection(1079 owner: T::CrossAccountId,1080 payer: T::CrossAccountId,1081 data: CreateCollectionData<T::AccountId>,1082 flags: CollectionFlags,1083 ) -> Result<CollectionId, DispatchError> {1084 {1085 ensure!(1086 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1087 Error::<T>::CollectionTokenPrefixLimitExceeded1088 );1089 }10901091 let created_count = <CreatedCollectionCount<T>>::get()1092 .01093 .checked_add(1)1094 .ok_or(ArithmeticError::Overflow)?;1095 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1096 let id = CollectionId(created_count);10971098 1099 ensure!(1100 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1101 <Error<T>>::TotalCollectionsLimitExceeded1102 );11031104 11051106 let collection = Collection {1107 owner: owner.as_sub().clone(),1108 name: data.name,1109 mode: data.mode.clone(),1110 description: data.description,1111 token_prefix: data.token_prefix,1112 sponsorship: data1113 .pending_sponsor1114 .map(SponsorshipState::Unconfirmed)1115 .unwrap_or_default(),1116 limits: data1117 .limits1118 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1119 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1120 permissions: data1121 .permissions1122 .map(|permissions| {1123 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1124 })1125 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1126 flags,1127 };11281129 let mut collection_properties = up_data_structs::CollectionProperties::get();1130 collection_properties1131 .try_set_from_iter(data.properties.into_iter())1132 .map_err(<Error<T>>::from)?;11331134 CollectionProperties::<T>::insert(id, collection_properties);11351136 let mut token_props_permissions = PropertiesPermissionMap::new();1137 token_props_permissions1138 .try_set_from_iter(data.token_property_permissions.into_iter())1139 .map_err(<Error<T>>::from)?;11401141 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11421143 1144 {1145 let mut imbalance =1146 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1147 imbalance.subsume(1148 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1149 &T::TreasuryAccountId::get(),1150 T::CollectionCreationPrice::get(),1151 ),1152 );1153 <T as Config>::Currency::settle(1154 payer.as_sub(),1155 imbalance,1156 WithdrawReasons::TRANSFER,1157 ExistenceRequirement::KeepAlive,1158 )1159 .map_err(|_| Error::<T>::NotSufficientFounds)?;1160 }11611162 <CreatedCollectionCount<T>>::put(created_count);1163 <Pallet<T>>::deposit_event(Event::CollectionCreated(1164 id,1165 data.mode.id(),1166 owner.as_sub().clone(),1167 ));1168 <PalletEvm<T>>::deposit_log(1169 erc::CollectionHelpersEvents::CollectionCreated {1170 owner: *owner.as_eth(),1171 collection_id: eth::collection_id_to_address(id),1172 }1173 .to_log(T::ContractAddress::get()),1174 );1175 <CollectionById<T>>::insert(id, collection);1176 Ok(id)1177 }11781179 1180 1181 1182 1183 pub fn destroy_collection(1184 collection: CollectionHandle<T>,1185 sender: &T::CrossAccountId,1186 ) -> DispatchResult {1187 ensure!(1188 collection.limits.owner_can_destroy(),1189 <Error<T>>::NoPermission,1190 );1191 collection.check_is_owner(sender)?;11921193 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1194 .01195 .checked_add(1)1196 .ok_or(ArithmeticError::Overflow)?;11971198 11991200 <DestroyedCollectionCount<T>>::put(destroyed_collections);1201 <CollectionById<T>>::remove(collection.id);1202 <AdminAmount<T>>::remove(collection.id);1203 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1204 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1205 <CollectionProperties<T>>::remove(collection.id);12061207 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));12081209 <PalletEvm<T>>::deposit_log(1210 erc::CollectionHelpersEvents::CollectionDestroyed {1211 collection_id: eth::collection_id_to_address(collection.id),1212 }1213 .to_log(T::ContractAddress::get()),1214 );1215 Ok(())1216 }12171218 1219 1220 1221 1222 1223 1224 1225 1226 #[transactional]1227 fn modify_collection_properties(1228 collection: &CollectionHandle<T>,1229 sender: &T::CrossAccountId,1230 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1231 ) -> DispatchResult {1232 collection.check_is_owner_or_admin(sender)?;12331234 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12351236 for (key, value) in properties_updates {1237 match value {1238 Some(value) => {1239 stored_properties1240 .try_set(key.clone(), value)1241 .map_err(<Error<T>>::from)?;12421243 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1244 <PalletEvm<T>>::deposit_log(1245 erc::CollectionHelpersEvents::CollectionChanged {1246 collection_id: eth::collection_id_to_address(collection.id),1247 }1248 .to_log(T::ContractAddress::get()),1249 );1250 }1251 None => {1252 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12531254 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1255 <PalletEvm<T>>::deposit_log(1256 erc::CollectionHelpersEvents::CollectionChanged {1257 collection_id: eth::collection_id_to_address(collection.id),1258 }1259 .to_log(T::ContractAddress::get()),1260 );1261 }1262 }1263 }12641265 <CollectionProperties<T>>::set(collection.id, stored_properties);12661267 Ok(())1268 }12691270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 pub fn modify_token_properties(1288 collection: &CollectionHandle<T>,1289 sender: &T::CrossAccountId,1290 token_id: TokenId,1291 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1292 is_token_create: bool,1293 mut stored_properties: Properties,1294 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1295 set_token_properties: impl FnOnce(Properties),1296 ) -> DispatchResult {1297 let is_collection_admin = collection.is_owner_or_admin(sender);1298 let permissions = Self::property_permissions(collection.id);12991300 for (key, value) in properties_updates {1301 let permission = permissions1302 .get(&key)1303 .cloned()1304 .unwrap_or_else(PropertyPermission::none);13051306 let is_property_exists = stored_properties.get(&key).is_some();13071308 match permission {1309 PropertyPermission { mutable: false, .. } if is_property_exists => {1310 return Err(<Error<T>>::NoPermission.into());1311 }13121313 PropertyPermission {1314 collection_admin,1315 token_owner,1316 ..1317 } => {1318 1319 let is_token_create =1320 is_token_create && (collection_admin || token_owner) && value.is_some();1321 if !(is_token_create1322 || (collection_admin && is_collection_admin)1323 || (token_owner && is_token_owner()?))1324 {1325 fail!(<Error<T>>::NoPermission);1326 }1327 }1328 }13291330 match value {1331 Some(value) => {1332 stored_properties1333 .try_set(key.clone(), value)1334 .map_err(<Error<T>>::from)?;13351336 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1337 }1338 None => {1339 stored_properties.remove(&key).map_err(<Error<T>>::from)?;13401341 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1342 }1343 }13441345 <PalletEvm<T>>::deposit_log(1346 CollectionHelpersEvents::TokenChanged {1347 collection_id: eth::collection_id_to_address(collection.id),1348 token_id: token_id.into(),1349 }1350 .to_log(T::ContractAddress::get()),1351 );1352 }13531354 set_token_properties(stored_properties);13551356 Ok(())1357 }13581359 1360 1361 1362 1363 1364 1365 pub fn set_allowance_for_all(1366 collection: &CollectionHandle<T>,1367 owner: &T::CrossAccountId,1368 operator: &T::CrossAccountId,1369 approve: bool,1370 set_allowance: impl FnOnce(),1371 log: evm_coder::ethereum::Log,1372 ) -> DispatchResult {1373 if collection.permissions.access() == AccessMode::AllowList {1374 collection.check_allowlist(owner)?;1375 collection.check_allowlist(operator)?;1376 }13771378 Self::ensure_correct_receiver(operator)?;13791380 set_allowance();13811382 <PalletEvm<T>>::deposit_log(log);1383 Self::deposit_event(Event::ApprovedForAll(1384 collection.id,1385 owner.clone(),1386 operator.clone(),1387 approve,1388 ));1389 Ok(())1390 }13911392 1393 1394 1395 1396 1397 pub fn set_collection_property(1398 collection: &CollectionHandle<T>,1399 sender: &T::CrossAccountId,1400 property: Property,1401 ) -> DispatchResult {1402 Self::set_collection_properties(collection, sender, [property].into_iter())1403 }14041405 1406 1407 1408 1409 1410 1411 pub fn set_scoped_collection_property(1412 collection_id: CollectionId,1413 scope: PropertyScope,1414 property: Property,1415 ) -> DispatchResult {1416 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1417 properties.try_scoped_set(scope, property.key, property.value)1418 })1419 .map_err(<Error<T>>::from)?;14201421 Ok(())1422 }14231424 1425 1426 1427 1428 1429 1430 pub fn set_scoped_collection_properties(1431 collection_id: CollectionId,1432 scope: PropertyScope,1433 properties: impl Iterator<Item = Property>,1434 ) -> DispatchResult {1435 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1436 stored_properties.try_scoped_set_from_iter(scope, properties)1437 })1438 .map_err(<Error<T>>::from)?;14391440 Ok(())1441 }14421443 1444 1445 1446 1447 1448 pub fn set_collection_properties(1449 collection: &CollectionHandle<T>,1450 sender: &T::CrossAccountId,1451 properties: impl Iterator<Item = Property>,1452 ) -> DispatchResult {1453 Self::modify_collection_properties(1454 collection,1455 sender,1456 properties.map(|property| (property.key, Some(property.value))),1457 )1458 }14591460 1461 1462 1463 1464 1465 pub fn delete_collection_property(1466 collection: &CollectionHandle<T>,1467 sender: &T::CrossAccountId,1468 property_key: PropertyKey,1469 ) -> DispatchResult {1470 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1471 }14721473 1474 1475 1476 1477 1478 pub fn delete_collection_properties(1479 collection: &CollectionHandle<T>,1480 sender: &T::CrossAccountId,1481 property_keys: impl Iterator<Item = PropertyKey>,1482 ) -> DispatchResult {1483 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1484 }14851486 1487 1488 1489 1490 1491 1492 pub fn set_property_permission_unchecked(1493 collection: CollectionId,1494 property_permission: PropertyKeyPermission,1495 ) -> DispatchResult {1496 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1497 permissions.try_set(property_permission.key, property_permission.permission)1498 })1499 .map_err(<Error<T>>::from)?;1500 Ok(())1501 }15021503 1504 1505 1506 1507 1508 pub fn set_property_permission(1509 collection: &CollectionHandle<T>,1510 sender: &T::CrossAccountId,1511 property_permission: PropertyKeyPermission,1512 ) -> DispatchResult {1513 Self::set_scoped_property_permission(1514 collection,1515 sender,1516 PropertyScope::None,1517 property_permission,1518 )1519 }15201521 1522 1523 1524 1525 1526 1527 pub fn set_scoped_property_permission(1528 collection: &CollectionHandle<T>,1529 sender: &T::CrossAccountId,1530 scope: PropertyScope,1531 property_permission: PropertyKeyPermission,1532 ) -> DispatchResult {1533 collection.check_is_owner_or_admin(sender)?;15341535 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1536 let current_permission = all_permissions.get(&property_permission.key);1537 if matches![1538 current_permission,1539 Some(PropertyPermission { mutable: false, .. })1540 ] {1541 return Err(<Error<T>>::NoPermission.into());1542 }15431544 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1545 let property_permission = property_permission.clone();1546 permissions.try_scoped_set(1547 scope,1548 property_permission.key,1549 property_permission.permission,1550 )1551 })1552 .map_err(<Error<T>>::from)?;15531554 Self::deposit_event(Event::PropertyPermissionSet(1555 collection.id,1556 property_permission.key,1557 ));1558 <PalletEvm<T>>::deposit_log(1559 erc::CollectionHelpersEvents::CollectionChanged {1560 collection_id: eth::collection_id_to_address(collection.id),1561 }1562 .to_log(T::ContractAddress::get()),1563 );15641565 Ok(())1566 }15671568 1569 1570 1571 1572 1573 #[transactional]1574 pub fn set_token_property_permissions(1575 collection: &CollectionHandle<T>,1576 sender: &T::CrossAccountId,1577 property_permissions: Vec<PropertyKeyPermission>,1578 ) -> DispatchResult {1579 Self::set_scoped_token_property_permissions(1580 collection,1581 sender,1582 PropertyScope::None,1583 property_permissions,1584 )1585 }15861587 1588 1589 1590 1591 1592 1593 #[transactional]1594 pub fn set_scoped_token_property_permissions(1595 collection: &CollectionHandle<T>,1596 sender: &T::CrossAccountId,1597 scope: PropertyScope,1598 property_permissions: Vec<PropertyKeyPermission>,1599 ) -> DispatchResult {1600 for prop_pemission in property_permissions {1601 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1602 }16031604 Ok(())1605 }16061607 1608 pub fn get_collection_property(1609 collection_id: CollectionId,1610 key: &PropertyKey,1611 ) -> Option<PropertyValue> {1612 Self::collection_properties(collection_id).get(key).cloned()1613 }16141615 1616 pub fn bytes_keys_to_property_keys(1617 keys: Vec<Vec<u8>>,1618 ) -> Result<Vec<PropertyKey>, DispatchError> {1619 keys.into_iter()1620 .map(|key| -> Result<PropertyKey, DispatchError> {1621 key.try_into()1622 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1623 })1624 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1625 }16261627 1628 pub fn filter_collection_properties(1629 collection_id: CollectionId,1630 keys: Option<Vec<PropertyKey>>,1631 ) -> Result<Vec<Property>, DispatchError> {1632 let properties = Self::collection_properties(collection_id);16331634 let properties = keys1635 .map(|keys| {1636 keys.into_iter()1637 .filter_map(|key| {1638 properties.get(&key).map(|value| Property {1639 key,1640 value: value.clone(),1641 })1642 })1643 .collect()1644 })1645 .unwrap_or_else(|| {1646 properties1647 .into_iter()1648 .map(|(key, value)| Property { key, value })1649 .collect()1650 });16511652 Ok(properties)1653 }16541655 1656 pub fn filter_property_permissions(1657 collection_id: CollectionId,1658 keys: Option<Vec<PropertyKey>>,1659 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1660 let permissions = Self::property_permissions(collection_id);16611662 let key_permissions = keys1663 .map(|keys| {1664 keys.into_iter()1665 .filter_map(|key| {1666 permissions1667 .get(&key)1668 .map(|permission| PropertyKeyPermission {1669 key,1670 permission: permission.clone(),1671 })1672 })1673 .collect()1674 })1675 .unwrap_or_else(|| {1676 permissions1677 .into_iter()1678 .map(|(key, permission)| PropertyKeyPermission { key, permission })1679 .collect()1680 });16811682 Ok(key_permissions)1683 }16841685 1686 1687 1688 pub fn toggle_allowlist(1689 collection: &CollectionHandle<T>,1690 sender: &T::CrossAccountId,1691 user: &T::CrossAccountId,1692 allowed: bool,1693 ) -> DispatchResult {1694 collection.check_is_owner_or_admin(sender)?;16951696 16971698 if allowed {1699 <Allowlist<T>>::insert((collection.id, user), true);1700 Self::deposit_event(Event::<T>::AllowListAddressAdded(1701 collection.id,1702 user.clone(),1703 ));1704 } else {1705 <Allowlist<T>>::remove((collection.id, user));1706 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1707 collection.id,1708 user.clone(),1709 ));1710 }17111712 <PalletEvm<T>>::deposit_log(1713 erc::CollectionHelpersEvents::CollectionChanged {1714 collection_id: eth::collection_id_to_address(collection.id),1715 }1716 .to_log(T::ContractAddress::get()),1717 );17181719 Ok(())1720 }17211722 1723 1724 1725 pub fn toggle_admin(1726 collection: &CollectionHandle<T>,1727 sender: &T::CrossAccountId,1728 user: &T::CrossAccountId,1729 admin: bool,1730 ) -> DispatchResult {1731 collection.check_is_internal()?;1732 collection.check_is_owner(sender)?;17331734 let is_admin = <IsAdmin<T>>::get((collection.id, user));1735 if is_admin == admin {1736 if admin {1737 return Ok(());1738 } else {1739 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1740 }1741 }1742 let amount = <AdminAmount<T>>::get(collection.id);17431744 17451746 if admin {1747 let amount = amount1748 .checked_add(1)1749 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1750 ensure!(1751 amount <= Self::collection_admins_limit(),1752 <Error<T>>::CollectionAdminCountExceeded,1753 );17541755 <AdminAmount<T>>::insert(collection.id, amount);1756 <IsAdmin<T>>::insert((collection.id, user), true);17571758 Self::deposit_event(Event::<T>::CollectionAdminAdded(1759 collection.id,1760 user.clone(),1761 ));1762 } else {1763 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1764 <IsAdmin<T>>::remove((collection.id, user));17651766 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1767 collection.id,1768 user.clone(),1769 ));1770 }17711772 <PalletEvm<T>>::deposit_log(1773 erc::CollectionHelpersEvents::CollectionChanged {1774 collection_id: eth::collection_id_to_address(collection.id),1775 }1776 .to_log(T::ContractAddress::get()),1777 );17781779 Ok(())1780 }17811782 1783 pub fn update_limits(1784 user: &T::CrossAccountId,1785 collection: &mut CollectionHandle<T>,1786 new_limit: CollectionLimits,1787 ) -> DispatchResult {1788 collection.check_is_internal()?;1789 collection.check_is_owner_or_admin(user)?;17901791 collection.limits =1792 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;17931794 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1795 <PalletEvm<T>>::deposit_log(1796 erc::CollectionHelpersEvents::CollectionChanged {1797 collection_id: eth::collection_id_to_address(collection.id),1798 }1799 .to_log(T::ContractAddress::get()),1800 );18011802 collection.save()1803 }18041805 1806 fn clamp_limits(1807 mode: CollectionMode,1808 old_limit: &CollectionLimits,1809 mut new_limit: CollectionLimits,1810 ) -> Result<CollectionLimits, DispatchError> {1811 let limits = old_limit;1812 limit_default!(old_limit, new_limit,1813 account_token_ownership_limit => ensure!(1814 new_limit <= MAX_TOKEN_OWNERSHIP,1815 <Error<T>>::CollectionLimitBoundsExceeded,1816 ),1817 sponsored_data_size => ensure!(1818 new_limit <= CUSTOM_DATA_LIMIT,1819 <Error<T>>::CollectionLimitBoundsExceeded,1820 ),18211822 sponsored_data_rate_limit => {},1823 token_limit => ensure!(1824 old_limit >= new_limit && new_limit > 0,1825 <Error<T>>::CollectionTokenLimitExceeded1826 ),18271828 sponsor_transfer_timeout(match mode {1829 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1830 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1831 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1832 }) => ensure!(1833 new_limit <= MAX_SPONSOR_TIMEOUT,1834 <Error<T>>::CollectionLimitBoundsExceeded,1835 ),1836 sponsor_approve_timeout => {},1837 owner_can_transfer => ensure!(1838 !limits.owner_can_transfer_instaled() ||1839 old_limit || !new_limit,1840 <Error<T>>::OwnerPermissionsCantBeReverted,1841 ),1842 owner_can_destroy => ensure!(1843 old_limit || !new_limit,1844 <Error<T>>::OwnerPermissionsCantBeReverted,1845 ),1846 transfers_enabled => {},1847 );1848 Ok(new_limit)1849 }18501851 1852 pub fn update_permissions(1853 user: &T::CrossAccountId,1854 collection: &mut CollectionHandle<T>,1855 new_permission: CollectionPermissions,1856 ) -> DispatchResult {1857 collection.check_is_internal()?;1858 collection.check_is_owner_or_admin(user)?;1859 collection.permissions = Self::clamp_permissions(1860 collection.mode.clone(),1861 &collection.permissions,1862 new_permission,1863 )?;18641865 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1866 <PalletEvm<T>>::deposit_log(1867 erc::CollectionHelpersEvents::CollectionChanged {1868 collection_id: eth::collection_id_to_address(collection.id),1869 }1870 .to_log(T::ContractAddress::get()),1871 );18721873 collection.save()1874 }18751876 1877 fn clamp_permissions(1878 _mode: CollectionMode,1879 old_permission: &CollectionPermissions,1880 mut new_permission: CollectionPermissions,1881 ) -> Result<CollectionPermissions, DispatchError> {1882 limit_default_clone!(old_permission, new_permission,1883 access => {},1884 mint_mode => {},1885 nesting => { },1886 );1887 Ok(new_permission)1888 }18891890 1891 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1892 CollectionProperties::<T>::mutate(collection_id, |properties| {1893 properties.recompute_consumed_space();1894 });18951896 Ok(())1897 }1898}189919001901#[macro_export]1902macro_rules! unsupported {1903 ($runtime:path) => {1904 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1905 };1906}190719081909pub trait CommonWeightInfo<CrossAccountId> {1910 1911 fn create_item(data: &CreateItemData) -> Weight {1912 Self::create_multiple_items(from_ref(data))1913 }19141915 1916 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;19171918 1919 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;19201921 1922 fn burn_item() -> Weight;19231924 1925 1926 1927 fn set_collection_properties(amount: u32) -> Weight;19281929 1930 1931 1932 fn delete_collection_properties(amount: u32) -> Weight;19331934 1935 1936 1937 fn set_token_properties(amount: u32) -> Weight;19381939 1940 1941 1942 fn delete_token_properties(amount: u32) -> Weight;19431944 1945 1946 1947 fn set_token_property_permissions(amount: u32) -> Weight;19481949 1950 fn transfer() -> Weight;19511952 1953 fn approve() -> Weight;19541955 1956 fn approve_from() -> Weight;19571958 1959 fn transfer_from() -> Weight;19601961 1962 fn burn_from() -> Weight;19631964 1965 1966 1967 1968 fn burn_recursively_self_raw() -> Weight;19691970 1971 1972 1973 fn burn_recursively_breadth_raw(amount: u32) -> Weight;19741975 1976 1977 1978 1979 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1980 Self::burn_recursively_self_raw()1981 .saturating_mul(max_selfs.max(1) as u64)1982 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1983 }19841985 1986 fn token_owner() -> Weight;19871988 1989 fn set_allowance_for_all() -> Weight;19901991 1992 fn force_repair_item() -> Weight;1993}199419951996pub trait RefungibleExtensionsWeightInfo {1997 1998 fn repartition() -> Weight;1999}200020012002200320042005pub trait CommonCollectionOperations<T: Config> {2006 2007 2008 2009 2010 2011 2012 fn create_item(2013 &self,2014 sender: T::CrossAccountId,2015 to: T::CrossAccountId,2016 data: CreateItemData,2017 nesting_budget: &dyn Budget,2018 ) -> DispatchResultWithPostInfo;20192020 2021 2022 2023 2024 2025 2026 fn create_multiple_items(2027 &self,2028 sender: T::CrossAccountId,2029 to: T::CrossAccountId,2030 data: Vec<CreateItemData>,2031 nesting_budget: &dyn Budget,2032 ) -> DispatchResultWithPostInfo;20332034 2035 2036 2037 2038 2039 2040 fn create_multiple_items_ex(2041 &self,2042 sender: T::CrossAccountId,2043 data: CreateItemExData<T::CrossAccountId>,2044 nesting_budget: &dyn Budget,2045 ) -> DispatchResultWithPostInfo;20462047 2048 2049 2050 2051 2052 fn burn_item(2053 &self,2054 sender: T::CrossAccountId,2055 token: TokenId,2056 amount: u128,2057 ) -> DispatchResultWithPostInfo;20582059 2060 2061 2062 2063 2064 2065 fn burn_item_recursively(2066 &self,2067 sender: T::CrossAccountId,2068 token: TokenId,2069 self_budget: &dyn Budget,2070 breadth_budget: &dyn Budget,2071 ) -> DispatchResultWithPostInfo;20722073 2074 2075 2076 2077 fn set_collection_properties(2078 &self,2079 sender: T::CrossAccountId,2080 properties: Vec<Property>,2081 ) -> DispatchResultWithPostInfo;20822083 2084 2085 2086 2087 fn delete_collection_properties(2088 &self,2089 sender: &T::CrossAccountId,2090 property_keys: Vec<PropertyKey>,2091 ) -> DispatchResultWithPostInfo;20922093 2094 2095 2096 2097 2098 2099 2100 2101 2102 fn set_token_properties(2103 &self,2104 sender: T::CrossAccountId,2105 token_id: TokenId,2106 properties: Vec<Property>,2107 budget: &dyn Budget,2108 ) -> DispatchResultWithPostInfo;21092110 2111 2112 2113 2114 2115 2116 2117 2118 2119 fn delete_token_properties(2120 &self,2121 sender: T::CrossAccountId,2122 token_id: TokenId,2123 property_keys: Vec<PropertyKey>,2124 budget: &dyn Budget,2125 ) -> DispatchResultWithPostInfo;21262127 2128 2129 2130 2131 2132 2133 fn set_token_property_permissions(2134 &self,2135 sender: &T::CrossAccountId,2136 property_permissions: Vec<PropertyKeyPermission>,2137 ) -> DispatchResultWithPostInfo;21382139 2140 2141 2142 2143 2144 2145 2146 fn transfer(2147 &self,2148 sender: T::CrossAccountId,2149 to: T::CrossAccountId,2150 token: TokenId,2151 amount: u128,2152 budget: &dyn Budget,2153 ) -> DispatchResultWithPostInfo;21542155 2156 2157 2158 2159 2160 2161 fn approve(2162 &self,2163 sender: T::CrossAccountId,2164 spender: T::CrossAccountId,2165 token: TokenId,2166 amount: u128,2167 ) -> DispatchResultWithPostInfo;21682169 2170 2171 2172 2173 2174 2175 2176 fn approve_from(2177 &self,2178 sender: T::CrossAccountId,2179 from: T::CrossAccountId,2180 to: T::CrossAccountId,2181 token: TokenId,2182 amount: u128,2183 ) -> DispatchResultWithPostInfo;21842185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 fn transfer_from(2196 &self,2197 sender: T::CrossAccountId,2198 from: T::CrossAccountId,2199 to: T::CrossAccountId,2200 token: TokenId,2201 amount: u128,2202 budget: &dyn Budget,2203 ) -> DispatchResultWithPostInfo;22042205 2206 2207 2208 2209 2210 2211 2212 2213 2214 fn burn_from(2215 &self,2216 sender: T::CrossAccountId,2217 from: T::CrossAccountId,2218 token: TokenId,2219 amount: u128,2220 budget: &dyn Budget,2221 ) -> DispatchResultWithPostInfo;22222223 2224 2225 2226 2227 2228 2229 fn check_nesting(2230 &self,2231 sender: T::CrossAccountId,2232 from: (CollectionId, TokenId),2233 under: TokenId,2234 budget: &dyn Budget,2235 ) -> DispatchResult;22362237 2238 2239 2240 2241 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22422243 2244 2245 2246 2247 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22482249 2250 2251 2252 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;22532254 2255 fn collection_tokens(&self) -> Vec<TokenId>;22562257 2258 2259 2260 fn token_exists(&self, token: TokenId) -> bool;22612262 2263 fn last_token_id(&self) -> TokenId;22642265 2266 2267 2268 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22692270 2271 2272 2273 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22742275 2276 2277 2278 2279 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22802281 2282 2283 2284 2285 2286 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22872288 2289 fn total_supply(&self) -> u32;22902291 2292 2293 2294 fn account_balance(&self, account: T::CrossAccountId) -> u32;22952296 2297 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;22982299 2300 fn total_pieces(&self, token: TokenId) -> Option<u128>;23012302 2303 2304 2305 2306 2307 fn allowance(2308 &self,2309 sender: T::CrossAccountId,2310 spender: T::CrossAccountId,2311 token: TokenId,2312 ) -> u128;23132314 2315 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;23162317 2318 2319 2320 2321 fn set_allowance_for_all(2322 &self,2323 owner: T::CrossAccountId,2324 operator: T::CrossAccountId,2325 approve: bool,2326 ) -> DispatchResultWithPostInfo;23272328 2329 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;23302331 2332 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2333}233423352336pub trait RefungibleExtensions<T>2337where2338 T: Config,2339{2340 2341 2342 2343 2344 2345 2346 2347 fn repartition(2348 &self,2349 sender: &T::CrossAccountId,2350 token: TokenId,2351 amount: u128,2352 ) -> DispatchResultWithPostInfo;2353}23542355235623572358pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2359 let post_info = PostDispatchInfo {2360 actual_weight: Some(weight),2361 pays_fee: Pays::Yes,2362 };2363 match res {2364 Ok(()) => Ok(post_info),2365 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2366 }2367}23682369impl<T: Config> From<PropertiesError> for Error<T> {2370 fn from(error: PropertiesError) -> Self {2371 match error {2372 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2373 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2374 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2375 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2376 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2377 }2378 }2379}