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, Properties, PropertiesPermissionMap, PropertyKey, PropertyValue,80 PropertyPermission, PropertiesError, TokenOwnerError, PropertyKeyPermission, TokenData,81 TrySetProperty, PropertyScope, CollectionPermissions,82};83use up_pov_estimate_rpc::PovInfo;8485pub use pallet::*;86use sp_core::H160;87use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};8889use crate::erc::CollectionHelpersEvents;90#[cfg(feature = "runtime-benchmarks")]91pub mod benchmarking;92pub mod dispatch;93pub mod erc;94pub mod eth;95pub mod weights;969798pub type SelfWeightOf<T> = <T as Config>::WeightInfo;99100101102103104105106#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]107pub struct CollectionHandle<T: Config> {108 109 pub id: CollectionId,110 collection: Collection<T::AccountId>,111 112 pub recorder: SubstrateRecorder<T>,113}114115impl<T: Config> WithRecorder<T> for CollectionHandle<T> {116 fn recorder(&self) -> &SubstrateRecorder<T> {117 &self.recorder118 }119 fn into_recorder(self) -> SubstrateRecorder<T> {120 self.recorder121 }122}123124impl<T: Config> CollectionHandle<T> {125 126 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {127 <CollectionById<T>>::get(id).map(|collection| Self {128 id,129 collection,130 recorder: SubstrateRecorder::new(gas_limit),131 })132 }133134 135 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {136 <CollectionById<T>>::get(id).map(|collection| Self {137 id,138 collection,139 recorder,140 })141 }142143 144 145 pub fn new(id: CollectionId) -> Option<Self> {146 Self::new_with_gas_limit(id, u64::MAX)147 }148149 150 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {151 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)152 }153154 155 pub fn consume_store_reads(156 &self,157 reads: u64,158 ) -> pallet_evm_coder_substrate::execution::Result<()> {159 self.recorder160 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(161 <T as frame_system::Config>::DbWeight::get()162 .read163 .saturating_mul(reads),164 )))165 }166167 168 pub fn consume_store_writes(169 &self,170 writes: u64,171 ) -> pallet_evm_coder_substrate::execution::Result<()> {172 self.recorder173 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(174 <T as frame_system::Config>::DbWeight::get()175 .write176 .saturating_mul(writes),177 )))178 }179180 181 pub fn consume_store_reads_and_writes(182 &self,183 reads: u64,184 writes: u64,185 ) -> pallet_evm_coder_substrate::execution::Result<()> {186 let weight = <T as frame_system::Config>::DbWeight::get();187 let reads = weight.read.saturating_mul(reads);188 let writes = weight.read.saturating_mul(writes);189 self.recorder190 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(191 reads.saturating_add(writes),192 )))193 }194195 196 pub fn save(&self) -> DispatchResult {197 <CollectionById<T>>::insert(self.id, &self.collection);198 Ok(())199 }200201 202 203 204 205 206 pub fn set_sponsor(207 &mut self,208 sender: &T::CrossAccountId,209 sponsor: T::AccountId,210 ) -> DispatchResult {211 self.check_is_internal()?;212 self.check_is_owner_or_admin(sender)?;213214 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());215216 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));217 <PalletEvm<T>>::deposit_log(218 erc::CollectionHelpersEvents::CollectionChanged {219 collection_id: eth::collection_id_to_address(self.id),220 }221 .to_log(T::ContractAddress::get()),222 );223224 self.save()225 }226227 228 229 230 231 232 233 234 235 236 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {237 self.check_is_internal()?;238239 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());240241 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));242 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));243 <PalletEvm<T>>::deposit_log(244 erc::CollectionHelpersEvents::CollectionChanged {245 collection_id: eth::collection_id_to_address(self.id),246 }247 .to_log(T::ContractAddress::get()),248 );249250 self.save()251 }252253 254 255 256 257 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {258 self.check_is_internal()?;259 ensure!(260 self.collection.sponsorship.pending_sponsor() == Some(sender),261 Error::<T>::ConfirmSponsorshipFail262 );263264 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());265266 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));267 <PalletEvm<T>>::deposit_log(268 erc::CollectionHelpersEvents::CollectionChanged {269 collection_id: eth::collection_id_to_address(self.id),270 }271 .to_log(T::ContractAddress::get()),272 );273274 self.save()275 }276277 278 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {279 self.check_is_internal()?;280 self.check_is_owner_or_admin(sender)?;281282 self.collection.sponsorship = SponsorshipState::Disabled;283284 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));285 <PalletEvm<T>>::deposit_log(286 erc::CollectionHelpersEvents::CollectionChanged {287 collection_id: eth::collection_id_to_address(self.id),288 }289 .to_log(T::ContractAddress::get()),290 );291 self.save()292 }293294 295 296 297 298 pub fn force_remove_sponsor(&mut self) -> DispatchResult {299 self.check_is_internal()?;300301 self.collection.sponsorship = SponsorshipState::Disabled;302303 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));304 <PalletEvm<T>>::deposit_log(305 erc::CollectionHelpersEvents::CollectionChanged {306 collection_id: eth::collection_id_to_address(self.id),307 }308 .to_log(T::ContractAddress::get()),309 );310 self.save()311 }312313 314 315 pub fn check_is_internal(&self) -> DispatchResult {316 if self.flags.external {317 return Err(<Error<T>>::CollectionIsExternal)?;318 }319320 Ok(())321 }322323 324 325 pub fn check_is_external(&self) -> DispatchResult {326 if !self.flags.external {327 return Err(<Error<T>>::CollectionIsInternal)?;328 }329330 Ok(())331 }332}333334impl<T: Config> Deref for CollectionHandle<T> {335 type Target = Collection<T::AccountId>;336337 fn deref(&self) -> &Self::Target {338 &self.collection339 }340}341342impl<T: Config> DerefMut for CollectionHandle<T> {343 fn deref_mut(&mut self) -> &mut Self::Target {344 &mut self.collection345 }346}347348impl<T: Config> CollectionHandle<T> {349 350 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {351 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);352 Ok(())353 }354355 356 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {357 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))358 }359360 361 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {362 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);363 Ok(())364 }365366 367 368 369 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {370 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)371 }372373 374 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {375 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)376 }377378 379 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {380 ensure!(381 <Allowlist<T>>::get((self.id, user)),382 <Error<T>>::AddressNotInAllowlist383 );384 Ok(())385 }386387 388 389 390 pub fn change_owner(391 &mut self,392 caller: T::CrossAccountId,393 new_owner: T::CrossAccountId,394 ) -> DispatchResult {395 self.check_is_internal()?;396 self.check_is_owner(&caller)?;397 self.collection.owner = new_owner.as_sub().clone();398399 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(400 self.id,401 new_owner.as_sub().clone(),402 ));403 <PalletEvm<T>>::deposit_log(404 erc::CollectionHelpersEvents::CollectionChanged {405 collection_id: eth::collection_id_to_address(self.id),406 }407 .to_log(T::ContractAddress::get()),408 );409410 self.save()411 }412}413414#[frame_support::pallet]415pub mod pallet {416 use super::*;417 use dispatch::CollectionDispatch;418 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};419 use frame_system::pallet_prelude::*;420 use frame_support::traits::Currency;421 use up_data_structs::{TokenId, mapping::TokenAddressMapping};422 use scale_info::TypeInfo;423 use weights::WeightInfo;424425 #[pallet::config]426 pub trait Config:427 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo428 {429 430 type WeightInfo: WeightInfo;431432 433 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;434435 436 type Currency: Currency<Self::AccountId>;437438 439 #[pallet::constant]440 type CollectionCreationPrice: Get<441 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,442 >;443444 445 type CollectionDispatch: CollectionDispatch<Self>;446447 448 type TreasuryAccountId: Get<Self::AccountId>;449450 451 #[pallet::constant]452 type ContractAddress: Get<H160>;453454 455 type EvmTokenAddressMapping: TokenAddressMapping<H160>;456457 458 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;459 }460461 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);462463 #[pallet::pallet]464 #[pallet::storage_version(STORAGE_VERSION)]465 #[pallet::generate_store(pub(super) trait Store)]466 pub struct Pallet<T>(_);467468 #[pallet::extra_constants]469 impl<T: Config> Pallet<T> {470 471 pub fn collection_admins_limit() -> u32 {472 COLLECTION_ADMINS_LIMIT473 }474 }475476 impl<T: Config> Pallet<T> {477 478 pub fn deposit_event(event: Event<T>) {479 let event = <T as Config>::RuntimeEvent::from(event);480 let event = event.into();481 <frame_system::Pallet<T>>::deposit_event(event)482 }483 }484485 #[pallet::event]486 pub enum Event<T: Config> {487 488 CollectionCreated(489 490 CollectionId,491 492 u8,493 494 T::AccountId,495 ),496497 498 CollectionDestroyed(499 500 CollectionId,501 ),502503 504 ItemCreated(505 506 CollectionId,507 508 TokenId,509 510 T::CrossAccountId,511 512 u128,513 ),514515 516 ItemDestroyed(517 518 CollectionId,519 520 TokenId,521 522 T::CrossAccountId,523 524 u128,525 ),526527 528 Transfer(529 530 CollectionId,531 532 TokenId,533 534 T::CrossAccountId,535 536 T::CrossAccountId,537 538 u128,539 ),540541 542 Approved(543 544 CollectionId,545 546 TokenId,547 548 T::CrossAccountId,549 550 T::CrossAccountId,551 552 u128,553 ),554555 556 ApprovedForAll(557 558 CollectionId,559 560 T::CrossAccountId,561 562 T::CrossAccountId,563 564 bool,565 ),566567 568 CollectionPropertySet(569 570 CollectionId,571 572 PropertyKey,573 ),574575 576 CollectionPropertyDeleted(577 578 CollectionId,579 580 PropertyKey,581 ),582583 584 TokenPropertySet(585 586 CollectionId,587 588 TokenId,589 590 PropertyKey,591 ),592593 594 TokenPropertyDeleted(595 596 CollectionId,597 598 TokenId,599 600 PropertyKey,601 ),602603 604 PropertyPermissionSet(605 606 CollectionId,607 608 PropertyKey,609 ),610611 612 AllowListAddressAdded(613 614 CollectionId,615 616 T::CrossAccountId,617 ),618619 620 AllowListAddressRemoved(621 622 CollectionId,623 624 T::CrossAccountId,625 ),626627 628 CollectionAdminAdded(629 630 CollectionId,631 632 T::CrossAccountId,633 ),634635 636 CollectionAdminRemoved(637 638 CollectionId,639 640 T::CrossAccountId,641 ),642643 644 CollectionLimitSet(645 646 CollectionId,647 ),648649 650 CollectionOwnerChanged(651 652 CollectionId,653 654 T::AccountId,655 ),656657 658 CollectionPermissionSet(659 660 CollectionId,661 ),662663 664 CollectionSponsorSet(665 666 CollectionId,667 668 T::AccountId,669 ),670671 672 SponsorshipConfirmed(673 674 CollectionId,675 676 T::AccountId,677 ),678679 680 CollectionSponsorRemoved(681 682 CollectionId,683 ),684 }685686 #[pallet::error]687 pub enum Error<T> {688 689 CollectionNotFound,690 691 MustBeTokenOwner,692 693 NoPermission,694 695 CantDestroyNotEmptyCollection,696 697 PublicMintingNotAllowed,698 699 AddressNotInAllowlist,700701 702 CollectionNameLimitExceeded,703 704 CollectionDescriptionLimitExceeded,705 706 CollectionTokenPrefixLimitExceeded,707 708 TotalCollectionsLimitExceeded,709 710 CollectionAdminCountExceeded,711 712 CollectionLimitBoundsExceeded,713 714 OwnerPermissionsCantBeReverted,715 716 TransferNotAllowed,717 718 AccountTokenLimitExceeded,719 720 CollectionTokenLimitExceeded,721 722 MetadataFlagFrozen,723724 725 TokenNotFound,726 727 TokenValueTooLow,728 729 ApprovedValueTooLow,730 731 CantApproveMoreThanOwned,732 733 AddressIsNotEthMirror,734735 736 AddressIsZero,737738 739 UnsupportedOperation,740741 742 NotSufficientFounds,743744 745 UserIsNotAllowedToNest,746 747 SourceCollectionIsNotAllowedToNest,748749 750 CollectionFieldSizeExceeded,751752 753 NoSpaceForProperty,754755 756 PropertyLimitReached,757758 759 PropertyKeyIsTooLong,760761 762 InvalidCharacterInPropertyKey,763764 765 EmptyPropertyKey,766767 768 CollectionIsExternal,769770 771 CollectionIsInternal,772773 774 ConfirmSponsorshipFail,775776 777 UserIsNotCollectionAdmin,778 }779780 781 #[pallet::storage]782 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;783784 785 #[pallet::storage]786 pub type DestroyedCollectionCount<T> =787 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;788789 790 #[pallet::storage]791 pub type CollectionById<T> = StorageMap<792 Hasher = Blake2_128Concat,793 Key = CollectionId,794 Value = Collection<<T as frame_system::Config>::AccountId>,795 QueryKind = OptionQuery,796 >;797798 799 #[pallet::storage]800 #[pallet::getter(fn collection_properties)]801 pub type CollectionProperties<T> = StorageMap<802 Hasher = Blake2_128Concat,803 Key = CollectionId,804 Value = Properties,805 QueryKind = ValueQuery,806 OnEmpty = up_data_structs::CollectionProperties,807 >;808809 810 #[pallet::storage]811 #[pallet::getter(fn property_permissions)]812 pub type CollectionPropertyPermissions<T> = StorageMap<813 Hasher = Blake2_128Concat,814 Key = CollectionId,815 Value = PropertiesPermissionMap,816 QueryKind = ValueQuery,817 >;818819 820 #[pallet::storage]821 pub type AdminAmount<T> = StorageMap<822 Hasher = Blake2_128Concat,823 Key = CollectionId,824 Value = u32,825 QueryKind = ValueQuery,826 >;827828 829 #[pallet::storage]830 pub type IsAdmin<T: Config> = StorageNMap<831 Key = (832 Key<Blake2_128Concat, CollectionId>,833 Key<Blake2_128Concat, T::CrossAccountId>,834 ),835 Value = bool,836 QueryKind = ValueQuery,837 >;838839 840 #[pallet::storage]841 pub type Allowlist<T: Config> = StorageNMap<842 Key = (843 Key<Blake2_128Concat, CollectionId>,844 Key<Blake2_128Concat, T::CrossAccountId>,845 ),846 Value = bool,847 QueryKind = ValueQuery,848 >;849850 851 #[pallet::storage]852 pub type DummyStorageValue<T: Config> = StorageValue<853 Value = (854 CollectionStats,855 CollectionId,856 TokenId,857 TokenChild,858 PhantomType<(859 TokenData<T::CrossAccountId>,860 RpcCollection<T::AccountId>,861 862 PovInfo,863 )>,864 ),865 QueryKind = OptionQuery,866 >;867868 #[pallet::hooks]869 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {870 fn on_runtime_upgrade() -> Weight {871 StorageVersion::new(1).put::<Pallet<T>>();872873 Weight::zero()874 }875 }876}877878impl<T: Config> Pallet<T> {879 880 881 882 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {883 ensure!(884 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,885 <Error<T>>::AddressIsZero886 );887 Ok(())888 }889890 891 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {892 <IsAdmin<T>>::iter_prefix((collection,))893 .map(|(a, _)| a)894 .collect()895 }896897 898 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {899 <Allowlist<T>>::iter_prefix((collection,))900 .map(|(a, _)| a)901 .collect()902 }903904 905 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {906 <Allowlist<T>>::get((collection, user))907 }908909 910 pub fn collection_stats() -> CollectionStats {911 let created = <CreatedCollectionCount<T>>::get();912 let destroyed = <DestroyedCollectionCount<T>>::get();913 CollectionStats {914 created: created.0,915 destroyed: destroyed.0,916 alive: created.0 - destroyed.0,917 }918 }919920 921 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {922 let collection = <CollectionById<T>>::get(collection)?;923 let limits = collection.limits;924 let effective_limits = CollectionLimits {925 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),926 sponsored_data_size: Some(limits.sponsored_data_size()),927 sponsored_data_rate_limit: Some(928 limits929 .sponsored_data_rate_limit930 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),931 ),932 token_limit: Some(limits.token_limit()),933 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(934 match collection.mode {935 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,936 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,937 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,938 },939 )),940 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),941 owner_can_transfer: Some(limits.owner_can_transfer()),942 owner_can_destroy: Some(limits.owner_can_destroy()),943 transfers_enabled: Some(limits.transfers_enabled()),944 };945946 Some(effective_limits)947 }948949 950 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {951 let Collection {952 name,953 description,954 owner,955 mode,956 token_prefix,957 sponsorship,958 limits,959 permissions,960 flags,961 } = <CollectionById<T>>::get(collection)?;962963 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)964 .into_iter()965 .map(|(key, permission)| PropertyKeyPermission { key, permission })966 .collect();967968 let properties = <CollectionProperties<T>>::get(collection)969 .into_iter()970 .map(|(key, value)| Property { key, value })971 .collect();972973 let permissions = CollectionPermissions {974 access: Some(permissions.access()),975 mint_mode: Some(permissions.mint_mode()),976 nesting: Some(permissions.nesting().clone()),977 };978979 Some(RpcCollection {980 name: name.into_inner(),981 description: description.into_inner(),982 owner,983 mode,984 token_prefix: token_prefix.into_inner(),985 sponsorship,986 limits,987 permissions,988 token_property_permissions,989 properties,990 read_only: flags.external,991992 flags: RpcCollectionFlags {993 foreign: flags.foreign,994 erc721metadata: flags.erc721metadata,995 },996 })997 }998}9991000macro_rules! limit_default {1001 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1002 $(1003 if let Some($new) = $new.$field {1004 let $old = $old.$field($($arg)?);1005 let _ = $new;1006 let _ = $old;1007 $check1008 } else {1009 $new.$field = $old.$field1010 }1011 )*1012 }};1013}1014macro_rules! limit_default_clone {1015 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1016 $(1017 if let Some($new) = $new.$field.clone() {1018 let $old = $old.$field($($arg)?);1019 let _ = $new;1020 let _ = $old;1021 $check1022 } else {1023 $new.$field = $old.$field.clone()1024 }1025 )*1026 }};1027}10281029impl<T: Config> Pallet<T> {1030 1031 1032 1033 1034 1035 pub fn init_collection(1036 owner: T::CrossAccountId,1037 payer: T::CrossAccountId,1038 data: CreateCollectionData<T::AccountId>,1039 flags: CollectionFlags,1040 ) -> Result<CollectionId, DispatchError> {1041 {1042 ensure!(1043 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1044 Error::<T>::CollectionTokenPrefixLimitExceeded1045 );1046 }10471048 let created_count = <CreatedCollectionCount<T>>::get()1049 .01050 .checked_add(1)1051 .ok_or(ArithmeticError::Overflow)?;1052 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1053 let id = CollectionId(created_count);10541055 1056 ensure!(1057 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1058 <Error<T>>::TotalCollectionsLimitExceeded1059 );10601061 10621063 let collection = Collection {1064 owner: owner.as_sub().clone(),1065 name: data.name,1066 mode: data.mode.clone(),1067 description: data.description,1068 token_prefix: data.token_prefix,1069 sponsorship: data1070 .pending_sponsor1071 .map(SponsorshipState::Unconfirmed)1072 .unwrap_or_default(),1073 limits: data1074 .limits1075 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1076 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1077 permissions: data1078 .permissions1079 .map(|permissions| {1080 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1081 })1082 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1083 flags,1084 };10851086 let mut collection_properties = up_data_structs::CollectionProperties::get();1087 collection_properties1088 .try_set_from_iter(data.properties.into_iter())1089 .map_err(<Error<T>>::from)?;10901091 CollectionProperties::<T>::insert(id, collection_properties);10921093 let mut token_props_permissions = PropertiesPermissionMap::new();1094 token_props_permissions1095 .try_set_from_iter(data.token_property_permissions.into_iter())1096 .map_err(<Error<T>>::from)?;10971098 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);10991100 1101 {1102 let mut imbalance =1103 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1104 imbalance.subsume(1105 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1106 &T::TreasuryAccountId::get(),1107 T::CollectionCreationPrice::get(),1108 ),1109 );1110 <T as Config>::Currency::settle(1111 payer.as_sub(),1112 imbalance,1113 WithdrawReasons::TRANSFER,1114 ExistenceRequirement::KeepAlive,1115 )1116 .map_err(|_| Error::<T>::NotSufficientFounds)?;1117 }11181119 <CreatedCollectionCount<T>>::put(created_count);1120 <Pallet<T>>::deposit_event(Event::CollectionCreated(1121 id,1122 data.mode.id(),1123 owner.as_sub().clone(),1124 ));1125 <PalletEvm<T>>::deposit_log(1126 erc::CollectionHelpersEvents::CollectionCreated {1127 owner: *owner.as_eth(),1128 collection_id: eth::collection_id_to_address(id),1129 }1130 .to_log(T::ContractAddress::get()),1131 );1132 <CollectionById<T>>::insert(id, collection);1133 Ok(id)1134 }11351136 1137 1138 1139 1140 pub fn destroy_collection(1141 collection: CollectionHandle<T>,1142 sender: &T::CrossAccountId,1143 ) -> DispatchResult {1144 ensure!(1145 collection.limits.owner_can_destroy(),1146 <Error<T>>::NoPermission,1147 );1148 collection.check_is_owner(sender)?;11491150 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1151 .01152 .checked_add(1)1153 .ok_or(ArithmeticError::Overflow)?;11541155 11561157 <DestroyedCollectionCount<T>>::put(destroyed_collections);1158 <CollectionById<T>>::remove(collection.id);1159 <AdminAmount<T>>::remove(collection.id);1160 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1161 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1162 <CollectionProperties<T>>::remove(collection.id);11631164 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11651166 <PalletEvm<T>>::deposit_log(1167 erc::CollectionHelpersEvents::CollectionDestroyed {1168 collection_id: eth::collection_id_to_address(collection.id),1169 }1170 .to_log(T::ContractAddress::get()),1171 );1172 Ok(())1173 }11741175 1176 1177 1178 1179 1180 1181 1182 1183 #[transactional]1184 fn modify_collection_properties(1185 collection: &CollectionHandle<T>,1186 sender: &T::CrossAccountId,1187 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1188 ) -> DispatchResult {1189 collection.check_is_owner_or_admin(sender)?;11901191 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);11921193 for (key, value) in properties_updates {1194 match value {1195 Some(value) => {1196 stored_properties1197 .try_set(key.clone(), value)1198 .map_err(<Error<T>>::from)?;11991200 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1201 <PalletEvm<T>>::deposit_log(1202 erc::CollectionHelpersEvents::CollectionChanged {1203 collection_id: eth::collection_id_to_address(collection.id),1204 }1205 .to_log(T::ContractAddress::get()),1206 );1207 }1208 None => {1209 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12101211 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1212 <PalletEvm<T>>::deposit_log(1213 erc::CollectionHelpersEvents::CollectionChanged {1214 collection_id: eth::collection_id_to_address(collection.id),1215 }1216 .to_log(T::ContractAddress::get()),1217 );1218 }1219 }1220 }12211222 <CollectionProperties<T>>::set(collection.id, stored_properties);12231224 Ok(())1225 }12261227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 pub fn modify_token_properties(1245 collection: &CollectionHandle<T>,1246 sender: &T::CrossAccountId,1247 token_id: TokenId,1248 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1249 is_token_create: bool,1250 mut stored_properties: Properties,1251 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1252 set_token_properties: impl FnOnce(Properties),1253 ) -> DispatchResult {1254 let is_collection_admin = collection.is_owner_or_admin(sender);1255 let permissions = Self::property_permissions(collection.id);12561257 let mut token_owner_result = None;1258 let mut is_token_owner = || -> Result<bool, DispatchError> {1259 *token_owner_result.get_or_insert_with(&is_token_owner)1260 };12611262 for (key, value) in properties_updates {1263 let permission = permissions1264 .get(&key)1265 .cloned()1266 .unwrap_or_else(PropertyPermission::none);12671268 let is_property_exists = stored_properties.get(&key).is_some();12691270 match permission {1271 PropertyPermission { mutable: false, .. } if is_property_exists => {1272 return Err(<Error<T>>::NoPermission.into());1273 }12741275 PropertyPermission {1276 collection_admin,1277 token_owner,1278 ..1279 } => {1280 1281 let is_token_create =1282 is_token_create && (collection_admin || token_owner) && value.is_some();1283 if !(is_token_create1284 || (collection_admin && is_collection_admin)1285 || (token_owner && is_token_owner()?))1286 {1287 fail!(<Error<T>>::NoPermission);1288 }1289 }1290 }12911292 match value {1293 Some(value) => {1294 stored_properties1295 .try_set(key.clone(), value)1296 .map_err(<Error<T>>::from)?;12971298 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1299 }1300 None => {1301 stored_properties.remove(&key).map_err(<Error<T>>::from)?;13021303 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1304 }1305 }13061307 <PalletEvm<T>>::deposit_log(1308 CollectionHelpersEvents::TokenChanged {1309 collection_id: eth::collection_id_to_address(collection.id),1310 token_id: token_id.into(),1311 }1312 .to_log(T::ContractAddress::get()),1313 );1314 }13151316 set_token_properties(stored_properties);13171318 Ok(())1319 }13201321 1322 1323 1324 1325 1326 1327 pub fn set_allowance_for_all(1328 collection: &CollectionHandle<T>,1329 owner: &T::CrossAccountId,1330 operator: &T::CrossAccountId,1331 approve: bool,1332 set_allowance: impl FnOnce(),1333 log: evm_coder::ethereum::Log,1334 ) -> DispatchResult {1335 if collection.permissions.access() == AccessMode::AllowList {1336 collection.check_allowlist(owner)?;1337 collection.check_allowlist(operator)?;1338 }13391340 Self::ensure_correct_receiver(operator)?;13411342 set_allowance();13431344 <PalletEvm<T>>::deposit_log(log);1345 Self::deposit_event(Event::ApprovedForAll(1346 collection.id,1347 owner.clone(),1348 operator.clone(),1349 approve,1350 ));1351 Ok(())1352 }13531354 1355 1356 1357 1358 1359 pub fn set_collection_property(1360 collection: &CollectionHandle<T>,1361 sender: &T::CrossAccountId,1362 property: Property,1363 ) -> DispatchResult {1364 Self::set_collection_properties(collection, sender, [property].into_iter())1365 }13661367 1368 1369 1370 1371 1372 1373 pub fn set_scoped_collection_property(1374 collection_id: CollectionId,1375 scope: PropertyScope,1376 property: Property,1377 ) -> DispatchResult {1378 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1379 properties.try_scoped_set(scope, property.key, property.value)1380 })1381 .map_err(<Error<T>>::from)?;13821383 Ok(())1384 }13851386 1387 1388 1389 1390 1391 1392 pub fn set_scoped_collection_properties(1393 collection_id: CollectionId,1394 scope: PropertyScope,1395 properties: impl Iterator<Item = Property>,1396 ) -> DispatchResult {1397 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1398 stored_properties.try_scoped_set_from_iter(scope, properties)1399 })1400 .map_err(<Error<T>>::from)?;14011402 Ok(())1403 }14041405 1406 1407 1408 1409 1410 pub fn set_collection_properties(1411 collection: &CollectionHandle<T>,1412 sender: &T::CrossAccountId,1413 properties: impl Iterator<Item = Property>,1414 ) -> DispatchResult {1415 Self::modify_collection_properties(1416 collection,1417 sender,1418 properties.map(|property| (property.key, Some(property.value))),1419 )1420 }14211422 1423 1424 1425 1426 1427 pub fn delete_collection_property(1428 collection: &CollectionHandle<T>,1429 sender: &T::CrossAccountId,1430 property_key: PropertyKey,1431 ) -> DispatchResult {1432 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1433 }14341435 1436 1437 1438 1439 1440 pub fn delete_collection_properties(1441 collection: &CollectionHandle<T>,1442 sender: &T::CrossAccountId,1443 property_keys: impl Iterator<Item = PropertyKey>,1444 ) -> DispatchResult {1445 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1446 }14471448 1449 1450 1451 1452 1453 1454 pub fn set_property_permission_unchecked(1455 collection: CollectionId,1456 property_permission: PropertyKeyPermission,1457 ) -> DispatchResult {1458 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1459 permissions.try_set(property_permission.key, property_permission.permission)1460 })1461 .map_err(<Error<T>>::from)?;1462 Ok(())1463 }14641465 1466 1467 1468 1469 1470 pub fn set_property_permission(1471 collection: &CollectionHandle<T>,1472 sender: &T::CrossAccountId,1473 property_permission: PropertyKeyPermission,1474 ) -> DispatchResult {1475 Self::set_scoped_property_permission(1476 collection,1477 sender,1478 PropertyScope::None,1479 property_permission,1480 )1481 }14821483 1484 1485 1486 1487 1488 1489 pub fn set_scoped_property_permission(1490 collection: &CollectionHandle<T>,1491 sender: &T::CrossAccountId,1492 scope: PropertyScope,1493 property_permission: PropertyKeyPermission,1494 ) -> DispatchResult {1495 collection.check_is_owner_or_admin(sender)?;14961497 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1498 let current_permission = all_permissions.get(&property_permission.key);1499 if matches![1500 current_permission,1501 Some(PropertyPermission { mutable: false, .. })1502 ] {1503 return Err(<Error<T>>::NoPermission.into());1504 }15051506 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1507 let property_permission = property_permission.clone();1508 permissions.try_scoped_set(1509 scope,1510 property_permission.key,1511 property_permission.permission,1512 )1513 })1514 .map_err(<Error<T>>::from)?;15151516 Self::deposit_event(Event::PropertyPermissionSet(1517 collection.id,1518 property_permission.key,1519 ));1520 <PalletEvm<T>>::deposit_log(1521 erc::CollectionHelpersEvents::CollectionChanged {1522 collection_id: eth::collection_id_to_address(collection.id),1523 }1524 .to_log(T::ContractAddress::get()),1525 );15261527 Ok(())1528 }15291530 1531 1532 1533 1534 1535 #[transactional]1536 pub fn set_token_property_permissions(1537 collection: &CollectionHandle<T>,1538 sender: &T::CrossAccountId,1539 property_permissions: Vec<PropertyKeyPermission>,1540 ) -> DispatchResult {1541 Self::set_scoped_token_property_permissions(1542 collection,1543 sender,1544 PropertyScope::None,1545 property_permissions,1546 )1547 }15481549 1550 1551 1552 1553 1554 1555 #[transactional]1556 pub fn set_scoped_token_property_permissions(1557 collection: &CollectionHandle<T>,1558 sender: &T::CrossAccountId,1559 scope: PropertyScope,1560 property_permissions: Vec<PropertyKeyPermission>,1561 ) -> DispatchResult {1562 for prop_pemission in property_permissions {1563 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1564 }15651566 Ok(())1567 }15681569 1570 pub fn get_collection_property(1571 collection_id: CollectionId,1572 key: &PropertyKey,1573 ) -> Option<PropertyValue> {1574 Self::collection_properties(collection_id).get(key).cloned()1575 }15761577 1578 pub fn bytes_keys_to_property_keys(1579 keys: Vec<Vec<u8>>,1580 ) -> Result<Vec<PropertyKey>, DispatchError> {1581 keys.into_iter()1582 .map(|key| -> Result<PropertyKey, DispatchError> {1583 key.try_into()1584 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1585 })1586 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1587 }15881589 1590 pub fn filter_collection_properties(1591 collection_id: CollectionId,1592 keys: Option<Vec<PropertyKey>>,1593 ) -> Result<Vec<Property>, DispatchError> {1594 let properties = Self::collection_properties(collection_id);15951596 let properties = keys1597 .map(|keys| {1598 keys.into_iter()1599 .filter_map(|key| {1600 properties.get(&key).map(|value| Property {1601 key,1602 value: value.clone(),1603 })1604 })1605 .collect()1606 })1607 .unwrap_or_else(|| {1608 properties1609 .into_iter()1610 .map(|(key, value)| Property { key, value })1611 .collect()1612 });16131614 Ok(properties)1615 }16161617 1618 pub fn filter_property_permissions(1619 collection_id: CollectionId,1620 keys: Option<Vec<PropertyKey>>,1621 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1622 let permissions = Self::property_permissions(collection_id);16231624 let key_permissions = keys1625 .map(|keys| {1626 keys.into_iter()1627 .filter_map(|key| {1628 permissions1629 .get(&key)1630 .map(|permission| PropertyKeyPermission {1631 key,1632 permission: permission.clone(),1633 })1634 })1635 .collect()1636 })1637 .unwrap_or_else(|| {1638 permissions1639 .into_iter()1640 .map(|(key, permission)| PropertyKeyPermission { key, permission })1641 .collect()1642 });16431644 Ok(key_permissions)1645 }16461647 1648 1649 1650 pub fn toggle_allowlist(1651 collection: &CollectionHandle<T>,1652 sender: &T::CrossAccountId,1653 user: &T::CrossAccountId,1654 allowed: bool,1655 ) -> DispatchResult {1656 collection.check_is_owner_or_admin(sender)?;16571658 16591660 if allowed {1661 <Allowlist<T>>::insert((collection.id, user), true);1662 Self::deposit_event(Event::<T>::AllowListAddressAdded(1663 collection.id,1664 user.clone(),1665 ));1666 } else {1667 <Allowlist<T>>::remove((collection.id, user));1668 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1669 collection.id,1670 user.clone(),1671 ));1672 }16731674 <PalletEvm<T>>::deposit_log(1675 erc::CollectionHelpersEvents::CollectionChanged {1676 collection_id: eth::collection_id_to_address(collection.id),1677 }1678 .to_log(T::ContractAddress::get()),1679 );16801681 Ok(())1682 }16831684 1685 1686 1687 pub fn toggle_admin(1688 collection: &CollectionHandle<T>,1689 sender: &T::CrossAccountId,1690 user: &T::CrossAccountId,1691 admin: bool,1692 ) -> DispatchResult {1693 collection.check_is_internal()?;1694 collection.check_is_owner(sender)?;16951696 let is_admin = <IsAdmin<T>>::get((collection.id, user));1697 if is_admin == admin {1698 if admin {1699 return Ok(());1700 } else {1701 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1702 }1703 }1704 let amount = <AdminAmount<T>>::get(collection.id);17051706 17071708 if admin {1709 let amount = amount1710 .checked_add(1)1711 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1712 ensure!(1713 amount <= Self::collection_admins_limit(),1714 <Error<T>>::CollectionAdminCountExceeded,1715 );17161717 <AdminAmount<T>>::insert(collection.id, amount);1718 <IsAdmin<T>>::insert((collection.id, user), true);17191720 Self::deposit_event(Event::<T>::CollectionAdminAdded(1721 collection.id,1722 user.clone(),1723 ));1724 } else {1725 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1726 <IsAdmin<T>>::remove((collection.id, user));17271728 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1729 collection.id,1730 user.clone(),1731 ));1732 }17331734 <PalletEvm<T>>::deposit_log(1735 erc::CollectionHelpersEvents::CollectionChanged {1736 collection_id: eth::collection_id_to_address(collection.id),1737 }1738 .to_log(T::ContractAddress::get()),1739 );17401741 Ok(())1742 }17431744 1745 pub fn update_limits(1746 user: &T::CrossAccountId,1747 collection: &mut CollectionHandle<T>,1748 new_limit: CollectionLimits,1749 ) -> DispatchResult {1750 collection.check_is_internal()?;1751 collection.check_is_owner_or_admin(user)?;17521753 collection.limits =1754 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;17551756 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1757 <PalletEvm<T>>::deposit_log(1758 erc::CollectionHelpersEvents::CollectionChanged {1759 collection_id: eth::collection_id_to_address(collection.id),1760 }1761 .to_log(T::ContractAddress::get()),1762 );17631764 collection.save()1765 }17661767 1768 fn clamp_limits(1769 mode: CollectionMode,1770 old_limit: &CollectionLimits,1771 mut new_limit: CollectionLimits,1772 ) -> Result<CollectionLimits, DispatchError> {1773 let limits = old_limit;1774 limit_default!(old_limit, new_limit,1775 account_token_ownership_limit => ensure!(1776 new_limit <= MAX_TOKEN_OWNERSHIP,1777 <Error<T>>::CollectionLimitBoundsExceeded,1778 ),1779 sponsored_data_size => ensure!(1780 new_limit <= CUSTOM_DATA_LIMIT,1781 <Error<T>>::CollectionLimitBoundsExceeded,1782 ),17831784 sponsored_data_rate_limit => {},1785 token_limit => ensure!(1786 old_limit >= new_limit && new_limit > 0,1787 <Error<T>>::CollectionTokenLimitExceeded1788 ),17891790 sponsor_transfer_timeout(match mode {1791 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1792 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1793 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1794 }) => ensure!(1795 new_limit <= MAX_SPONSOR_TIMEOUT,1796 <Error<T>>::CollectionLimitBoundsExceeded,1797 ),1798 sponsor_approve_timeout => {},1799 owner_can_transfer => ensure!(1800 !limits.owner_can_transfer_instaled() ||1801 old_limit || !new_limit,1802 <Error<T>>::OwnerPermissionsCantBeReverted,1803 ),1804 owner_can_destroy => ensure!(1805 old_limit || !new_limit,1806 <Error<T>>::OwnerPermissionsCantBeReverted,1807 ),1808 transfers_enabled => {},1809 );1810 Ok(new_limit)1811 }18121813 1814 pub fn update_permissions(1815 user: &T::CrossAccountId,1816 collection: &mut CollectionHandle<T>,1817 new_permission: CollectionPermissions,1818 ) -> DispatchResult {1819 collection.check_is_internal()?;1820 collection.check_is_owner_or_admin(user)?;1821 collection.permissions = Self::clamp_permissions(1822 collection.mode.clone(),1823 &collection.permissions,1824 new_permission,1825 )?;18261827 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1828 <PalletEvm<T>>::deposit_log(1829 erc::CollectionHelpersEvents::CollectionChanged {1830 collection_id: eth::collection_id_to_address(collection.id),1831 }1832 .to_log(T::ContractAddress::get()),1833 );18341835 collection.save()1836 }18371838 1839 fn clamp_permissions(1840 _mode: CollectionMode,1841 old_permission: &CollectionPermissions,1842 mut new_permission: CollectionPermissions,1843 ) -> Result<CollectionPermissions, DispatchError> {1844 limit_default_clone!(old_permission, new_permission,1845 access => {},1846 mint_mode => {},1847 nesting => { },1848 );1849 Ok(new_permission)1850 }18511852 1853 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1854 CollectionProperties::<T>::mutate(collection_id, |properties| {1855 properties.recompute_consumed_space();1856 });18571858 Ok(())1859 }1860}186118621863#[macro_export]1864macro_rules! unsupported {1865 ($runtime:path) => {1866 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1867 };1868}186918701871pub trait CommonWeightInfo<CrossAccountId> {1872 1873 fn create_item(data: &CreateItemData) -> Weight {1874 Self::create_multiple_items(from_ref(data))1875 }18761877 1878 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;18791880 1881 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;18821883 1884 fn burn_item() -> Weight;18851886 1887 1888 1889 fn set_collection_properties(amount: u32) -> Weight;18901891 1892 1893 1894 fn delete_collection_properties(amount: u32) -> Weight;18951896 1897 1898 1899 fn set_token_properties(amount: u32) -> Weight;19001901 1902 1903 1904 fn delete_token_properties(amount: u32) -> Weight;19051906 1907 1908 1909 fn set_token_property_permissions(amount: u32) -> Weight;19101911 1912 fn transfer() -> Weight;19131914 1915 fn approve() -> Weight;19161917 1918 fn approve_from() -> Weight;19191920 1921 fn transfer_from() -> Weight;19221923 1924 fn burn_from() -> Weight;19251926 1927 1928 1929 1930 fn burn_recursively_self_raw() -> Weight;19311932 1933 1934 1935 fn burn_recursively_breadth_raw(amount: u32) -> Weight;19361937 1938 1939 1940 1941 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1942 Self::burn_recursively_self_raw()1943 .saturating_mul(max_selfs.max(1) as u64)1944 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1945 }19461947 1948 fn token_owner() -> Weight;19491950 1951 fn set_allowance_for_all() -> Weight;19521953 1954 fn force_repair_item() -> Weight;1955}195619571958pub trait RefungibleExtensionsWeightInfo {1959 1960 fn repartition() -> Weight;1961}196219631964196519661967pub trait CommonCollectionOperations<T: Config> {1968 1969 1970 1971 1972 1973 1974 fn create_item(1975 &self,1976 sender: T::CrossAccountId,1977 to: T::CrossAccountId,1978 data: CreateItemData,1979 nesting_budget: &dyn Budget,1980 ) -> DispatchResultWithPostInfo;19811982 1983 1984 1985 1986 1987 1988 fn create_multiple_items(1989 &self,1990 sender: T::CrossAccountId,1991 to: T::CrossAccountId,1992 data: Vec<CreateItemData>,1993 nesting_budget: &dyn Budget,1994 ) -> DispatchResultWithPostInfo;19951996 1997 1998 1999 2000 2001 2002 fn create_multiple_items_ex(2003 &self,2004 sender: T::CrossAccountId,2005 data: CreateItemExData<T::CrossAccountId>,2006 nesting_budget: &dyn Budget,2007 ) -> DispatchResultWithPostInfo;20082009 2010 2011 2012 2013 2014 fn burn_item(2015 &self,2016 sender: T::CrossAccountId,2017 token: TokenId,2018 amount: u128,2019 ) -> DispatchResultWithPostInfo;20202021 2022 2023 2024 2025 2026 2027 fn burn_item_recursively(2028 &self,2029 sender: T::CrossAccountId,2030 token: TokenId,2031 self_budget: &dyn Budget,2032 breadth_budget: &dyn Budget,2033 ) -> DispatchResultWithPostInfo;20342035 2036 2037 2038 2039 fn set_collection_properties(2040 &self,2041 sender: T::CrossAccountId,2042 properties: Vec<Property>,2043 ) -> DispatchResultWithPostInfo;20442045 2046 2047 2048 2049 fn delete_collection_properties(2050 &self,2051 sender: &T::CrossAccountId,2052 property_keys: Vec<PropertyKey>,2053 ) -> DispatchResultWithPostInfo;20542055 2056 2057 2058 2059 2060 2061 2062 2063 2064 fn set_token_properties(2065 &self,2066 sender: T::CrossAccountId,2067 token_id: TokenId,2068 properties: Vec<Property>,2069 budget: &dyn Budget,2070 ) -> DispatchResultWithPostInfo;20712072 2073 2074 2075 2076 2077 2078 2079 2080 2081 fn delete_token_properties(2082 &self,2083 sender: T::CrossAccountId,2084 token_id: TokenId,2085 property_keys: Vec<PropertyKey>,2086 budget: &dyn Budget,2087 ) -> DispatchResultWithPostInfo;20882089 2090 2091 2092 2093 2094 2095 fn set_token_property_permissions(2096 &self,2097 sender: &T::CrossAccountId,2098 property_permissions: Vec<PropertyKeyPermission>,2099 ) -> DispatchResultWithPostInfo;21002101 2102 2103 2104 2105 2106 2107 2108 fn transfer(2109 &self,2110 sender: T::CrossAccountId,2111 to: T::CrossAccountId,2112 token: TokenId,2113 amount: u128,2114 budget: &dyn Budget,2115 ) -> DispatchResultWithPostInfo;21162117 2118 2119 2120 2121 2122 2123 fn approve(2124 &self,2125 sender: T::CrossAccountId,2126 spender: T::CrossAccountId,2127 token: TokenId,2128 amount: u128,2129 ) -> DispatchResultWithPostInfo;21302131 2132 2133 2134 2135 2136 2137 2138 fn approve_from(2139 &self,2140 sender: T::CrossAccountId,2141 from: T::CrossAccountId,2142 to: T::CrossAccountId,2143 token: TokenId,2144 amount: u128,2145 ) -> DispatchResultWithPostInfo;21462147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 fn transfer_from(2158 &self,2159 sender: T::CrossAccountId,2160 from: T::CrossAccountId,2161 to: T::CrossAccountId,2162 token: TokenId,2163 amount: u128,2164 budget: &dyn Budget,2165 ) -> DispatchResultWithPostInfo;21662167 2168 2169 2170 2171 2172 2173 2174 2175 2176 fn burn_from(2177 &self,2178 sender: T::CrossAccountId,2179 from: T::CrossAccountId,2180 token: TokenId,2181 amount: u128,2182 budget: &dyn Budget,2183 ) -> DispatchResultWithPostInfo;21842185 2186 2187 2188 2189 2190 2191 fn check_nesting(2192 &self,2193 sender: T::CrossAccountId,2194 from: (CollectionId, TokenId),2195 under: TokenId,2196 budget: &dyn Budget,2197 ) -> DispatchResult;21982199 2200 2201 2202 2203 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22042205 2206 2207 2208 2209 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22102211 2212 2213 2214 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;22152216 2217 fn collection_tokens(&self) -> Vec<TokenId>;22182219 2220 2221 2222 fn token_exists(&self, token: TokenId) -> bool;22232224 2225 fn last_token_id(&self) -> TokenId;22262227 2228 2229 2230 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22312232 2233 2234 2235 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22362237 2238 2239 2240 2241 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22422243 2244 2245 2246 2247 2248 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22492250 2251 fn total_supply(&self) -> u32;22522253 2254 2255 2256 fn account_balance(&self, account: T::CrossAccountId) -> u32;22572258 2259 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;22602261 2262 fn total_pieces(&self, token: TokenId) -> Option<u128>;22632264 2265 2266 2267 2268 2269 fn allowance(2270 &self,2271 sender: T::CrossAccountId,2272 spender: T::CrossAccountId,2273 token: TokenId,2274 ) -> u128;22752276 2277 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;22782279 2280 2281 2282 2283 fn set_allowance_for_all(2284 &self,2285 owner: T::CrossAccountId,2286 operator: T::CrossAccountId,2287 approve: bool,2288 ) -> DispatchResultWithPostInfo;22892290 2291 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;22922293 2294 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2295}229622972298pub trait RefungibleExtensions<T>2299where2300 T: Config,2301{2302 2303 2304 2305 2306 2307 2308 2309 fn repartition(2310 &self,2311 sender: &T::CrossAccountId,2312 token: TokenId,2313 amount: u128,2314 ) -> DispatchResultWithPostInfo;2315}23162317231823192320pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2321 let post_info = PostDispatchInfo {2322 actual_weight: Some(weight),2323 pays_fee: Pays::Yes,2324 };2325 match res {2326 Ok(()) => Ok(post_info),2327 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2328 }2329}23302331impl<T: Config> From<PropertiesError> for Error<T> {2332 fn from(error: PropertiesError) -> Self {2333 match error {2334 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2335 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2336 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2337 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2338 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2339 }2340 }2341}