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::{68 Get,69 fungible::{Balanced, Debt, Inspect},70 tokens::{Imbalance, Precision, Preservation},71 },72 dispatch::Pays,73 transactional, fail,74};75use pallet_evm::GasWeightMapping;76use up_data_structs::{77 AccessMode, COLLECTION_NUMBER_LIMIT, Collection, RpcCollection, CollectionFlags,78 RpcCollectionFlags, CollectionId, CreateItemData, MAX_TOKEN_PREFIX_LENGTH,79 COLLECTION_ADMINS_LIMIT, TokenId, TokenChild, CollectionStats, MAX_TOKEN_OWNERSHIP,80 CollectionMode, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,81 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,82 CreateCollectionData, SponsorshipState, CreateItemExData, SponsoringRateLimit, budget::Budget,83 PhantomType, Property, CollectionProperties as CollectionPropertiesT, TokenProperties,84 PropertiesPermissionMap, PropertyKey, PropertyValue, PropertyPermission, PropertiesError,85 TokenOwnerError, PropertyKeyPermission, TokenData, TrySetProperty, PropertyScope,86 CollectionPermissions,87};88use up_pov_estimate_rpc::PovInfo;8990pub use pallet::*;91use sp_core::H160;92use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, traits::Zero};9394#[cfg(feature = "runtime-benchmarks")]95pub mod benchmarking;96pub mod dispatch;97pub mod erc;98pub mod eth;99pub mod helpers;100#[allow(missing_docs)]101pub mod weights;102103pub type SelfWeightOf<T> = <T as Config>::WeightInfo;104105106107108109110111#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]112pub struct CollectionHandle<T: Config> {113 114 pub id: CollectionId,115 collection: Collection<T::AccountId>,116 117 pub recorder: SubstrateRecorder<T>,118}119120impl<T: Config> WithRecorder<T> for CollectionHandle<T> {121 fn recorder(&self) -> &SubstrateRecorder<T> {122 &self.recorder123 }124 fn into_recorder(self) -> SubstrateRecorder<T> {125 self.recorder126 }127}128129impl<T: Config> CollectionHandle<T> {130 131 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {132 <CollectionById<T>>::get(id).map(|collection| Self {133 id,134 collection,135 recorder: SubstrateRecorder::new(gas_limit),136 })137 }138139 140 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {141 <CollectionById<T>>::get(id).map(|collection| Self {142 id,143 collection,144 recorder,145 })146 }147148 149 150 pub fn new(id: CollectionId) -> Option<Self> {151 Self::new_with_gas_limit(id, u64::MAX)152 }153154 155 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {156 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)157 }158159 160 pub fn consume_store_reads(161 &self,162 reads: u64,163 ) -> pallet_evm_coder_substrate::execution::Result<()> {164 self.recorder165 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(166 <T as frame_system::Config>::DbWeight::get()167 .read168 .saturating_mul(reads),169 170 0,171 )))172 }173174 175 pub fn consume_store_writes(176 &self,177 writes: u64,178 ) -> pallet_evm_coder_substrate::execution::Result<()> {179 self.recorder180 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(181 <T as frame_system::Config>::DbWeight::get()182 .write183 .saturating_mul(writes),184 185 0,186 )))187 }188189 190 pub fn consume_store_reads_and_writes(191 &self,192 reads: u64,193 writes: u64,194 ) -> pallet_evm_coder_substrate::execution::Result<()> {195 let weight = <T as frame_system::Config>::DbWeight::get();196 let reads = weight.read.saturating_mul(reads);197 let writes = weight.read.saturating_mul(writes);198 self.recorder199 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(200 reads.saturating_add(writes),201 202 0,203 )))204 }205206 207 pub fn save(&self) -> DispatchResult {208 <CollectionById<T>>::insert(self.id, &self.collection);209 Ok(())210 }211212 213 214 215 216 217 pub fn set_sponsor(218 &mut self,219 sender: &T::CrossAccountId,220 sponsor: T::AccountId,221 ) -> DispatchResult {222 self.check_is_internal()?;223 self.check_is_owner_or_admin(sender)?;224225 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());226227 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));228 <PalletEvm<T>>::deposit_log(229 erc::CollectionHelpersEvents::CollectionChanged {230 collection_id: eth::collection_id_to_address(self.id),231 }232 .to_log(T::ContractAddress::get()),233 );234235 self.save()236 }237238 239 240 241 242 243 244 245 246 247 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {248 self.check_is_internal()?;249250 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());251252 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));253 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));254 <PalletEvm<T>>::deposit_log(255 erc::CollectionHelpersEvents::CollectionChanged {256 collection_id: eth::collection_id_to_address(self.id),257 }258 .to_log(T::ContractAddress::get()),259 );260261 self.save()262 }263264 265 266 267 268 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {269 self.check_is_internal()?;270 ensure!(271 self.collection.sponsorship.pending_sponsor() == Some(sender),272 Error::<T>::ConfirmSponsorshipFail273 );274275 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());276277 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));278 <PalletEvm<T>>::deposit_log(279 erc::CollectionHelpersEvents::CollectionChanged {280 collection_id: eth::collection_id_to_address(self.id),281 }282 .to_log(T::ContractAddress::get()),283 );284285 self.save()286 }287288 289 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {290 self.check_is_internal()?;291 self.check_is_owner_or_admin(sender)?;292293 self.collection.sponsorship = SponsorshipState::Disabled;294295 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));296 <PalletEvm<T>>::deposit_log(297 erc::CollectionHelpersEvents::CollectionChanged {298 collection_id: eth::collection_id_to_address(self.id),299 }300 .to_log(T::ContractAddress::get()),301 );302 self.save()303 }304305 306 307 308 309 pub fn force_remove_sponsor(&mut self) -> DispatchResult {310 self.check_is_internal()?;311312 self.collection.sponsorship = SponsorshipState::Disabled;313314 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));315 <PalletEvm<T>>::deposit_log(316 erc::CollectionHelpersEvents::CollectionChanged {317 collection_id: eth::collection_id_to_address(self.id),318 }319 .to_log(T::ContractAddress::get()),320 );321 self.save()322 }323324 325 326 pub fn check_is_internal(&self) -> DispatchResult {327 if self.flags.external {328 return Err(<Error<T>>::CollectionIsExternal)?;329 }330331 Ok(())332 }333334 335 336 pub fn check_is_external(&self) -> DispatchResult {337 if !self.flags.external {338 return Err(<Error<T>>::CollectionIsInternal)?;339 }340341 Ok(())342 }343}344345impl<T: Config> Deref for CollectionHandle<T> {346 type Target = Collection<T::AccountId>;347348 fn deref(&self) -> &Self::Target {349 &self.collection350 }351}352353impl<T: Config> DerefMut for CollectionHandle<T> {354 fn deref_mut(&mut self) -> &mut Self::Target {355 &mut self.collection356 }357}358359impl<T: Config> CollectionHandle<T> {360 361 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {362 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);363 Ok(())364 }365366 367 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {368 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))369 }370371 372 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {373 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);374 Ok(())375 }376377 378 379 380 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {381 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)382 }383384 385 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {386 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)387 }388389 390 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {391 ensure!(392 <Allowlist<T>>::get((self.id, user)),393 <Error<T>>::AddressNotInAllowlist394 );395 Ok(())396 }397398 399 400 401 pub fn change_owner(402 &mut self,403 caller: T::CrossAccountId,404 new_owner: T::CrossAccountId,405 ) -> DispatchResult {406 self.check_is_internal()?;407 self.check_is_owner(&caller)?;408 self.collection.owner = new_owner.as_sub().clone();409410 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(411 self.id,412 new_owner.as_sub().clone(),413 ));414 <PalletEvm<T>>::deposit_log(415 erc::CollectionHelpersEvents::CollectionChanged {416 collection_id: eth::collection_id_to_address(self.id),417 }418 .to_log(T::ContractAddress::get()),419 );420421 self.save()422 }423}424425#[frame_support::pallet]426pub mod pallet {427428 use super::*;429 use dispatch::CollectionDispatch;430 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};431 use up_data_structs::{TokenId, mapping::TokenAddressMapping};432 use scale_info::TypeInfo;433 use weights::WeightInfo;434435 #[pallet::config]436 pub trait Config:437 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo438 {439 440 type WeightInfo: WeightInfo;441442 443 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;444445 446 type Currency: Balanced<Self::AccountId> + Inspect<Self::AccountId>;447448 449 #[pallet::constant]450 type CollectionCreationPrice: Get<451 <<Self as Config>::Currency as Inspect<Self::AccountId>>::Balance,452 >;453454 455 type CollectionDispatch: CollectionDispatch<Self>;456457 458 type TreasuryAccountId: Get<Self::AccountId>;459460 461 #[pallet::constant]462 type ContractAddress: Get<H160>;463464 465 type EvmTokenAddressMapping: TokenAddressMapping<H160>;466467 468 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;469 }470471 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);472473 #[pallet::pallet]474 #[pallet::storage_version(STORAGE_VERSION)]475 pub struct Pallet<T>(_);476477 #[pallet::extra_constants]478 impl<T: Config> Pallet<T> {479 480 pub fn collection_admins_limit() -> u32 {481 COLLECTION_ADMINS_LIMIT482 }483 }484485 #[pallet::genesis_config]486 pub struct GenesisConfig<T>(PhantomData<T>);487488 #[cfg(feature = "std")]489 impl<T: Config> Default for GenesisConfig<T> {490 fn default() -> Self {491 Self(Default::default())492 }493 }494495 #[pallet::genesis_build]496 impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {497 fn build(&self) {498 StorageVersion::new(1).put::<Pallet<T>>();499 }500 }501502 impl<T: Config> Pallet<T> {503 504 pub fn deposit_event(event: Event<T>) {505 let event = <T as Config>::RuntimeEvent::from(event);506 let event = event.into();507 <frame_system::Pallet<T>>::deposit_event(event)508 }509 }510511 #[pallet::event]512 pub enum Event<T: Config> {513 514 CollectionCreated(515 516 CollectionId,517 518 u8,519 520 T::AccountId,521 ),522523 524 CollectionDestroyed(525 526 CollectionId,527 ),528529 530 ItemCreated(531 532 CollectionId,533 534 TokenId,535 536 T::CrossAccountId,537 538 u128,539 ),540541 542 ItemDestroyed(543 544 CollectionId,545 546 TokenId,547 548 T::CrossAccountId,549 550 u128,551 ),552553 554 Transfer(555 556 CollectionId,557 558 TokenId,559 560 T::CrossAccountId,561 562 T::CrossAccountId,563 564 u128,565 ),566567 568 Approved(569 570 CollectionId,571 572 TokenId,573 574 T::CrossAccountId,575 576 T::CrossAccountId,577 578 u128,579 ),580581 582 ApprovedForAll(583 584 CollectionId,585 586 T::CrossAccountId,587 588 T::CrossAccountId,589 590 bool,591 ),592593 594 CollectionPropertySet(595 596 CollectionId,597 598 PropertyKey,599 ),600601 602 CollectionPropertyDeleted(603 604 CollectionId,605 606 PropertyKey,607 ),608609 610 TokenPropertySet(611 612 CollectionId,613 614 TokenId,615 616 PropertyKey,617 ),618619 620 TokenPropertyDeleted(621 622 CollectionId,623 624 TokenId,625 626 PropertyKey,627 ),628629 630 PropertyPermissionSet(631 632 CollectionId,633 634 PropertyKey,635 ),636637 638 AllowListAddressAdded(639 640 CollectionId,641 642 T::CrossAccountId,643 ),644645 646 AllowListAddressRemoved(647 648 CollectionId,649 650 T::CrossAccountId,651 ),652653 654 CollectionAdminAdded(655 656 CollectionId,657 658 T::CrossAccountId,659 ),660661 662 CollectionAdminRemoved(663 664 CollectionId,665 666 T::CrossAccountId,667 ),668669 670 CollectionLimitSet(671 672 CollectionId,673 ),674675 676 CollectionOwnerChanged(677 678 CollectionId,679 680 T::AccountId,681 ),682683 684 CollectionPermissionSet(685 686 CollectionId,687 ),688689 690 CollectionSponsorSet(691 692 CollectionId,693 694 T::AccountId,695 ),696697 698 SponsorshipConfirmed(699 700 CollectionId,701 702 T::AccountId,703 ),704705 706 CollectionSponsorRemoved(707 708 CollectionId,709 ),710 }711712 #[pallet::error]713 pub enum Error<T> {714 715 CollectionNotFound,716 717 MustBeTokenOwner,718 719 NoPermission,720 721 CantDestroyNotEmptyCollection,722 723 PublicMintingNotAllowed,724 725 AddressNotInAllowlist,726727 728 CollectionNameLimitExceeded,729 730 CollectionDescriptionLimitExceeded,731 732 CollectionTokenPrefixLimitExceeded,733 734 TotalCollectionsLimitExceeded,735 736 CollectionAdminCountExceeded,737 738 CollectionLimitBoundsExceeded,739 740 OwnerPermissionsCantBeReverted,741 742 TransferNotAllowed,743 744 AccountTokenLimitExceeded,745 746 CollectionTokenLimitExceeded,747 748 MetadataFlagFrozen,749750 751 TokenNotFound,752 753 TokenValueTooLow,754 755 ApprovedValueTooLow,756 757 CantApproveMoreThanOwned,758 759 AddressIsNotEthMirror,760761 762 AddressIsZero,763764 765 UnsupportedOperation,766767 768 NotSufficientFounds,769770 771 UserIsNotAllowedToNest,772 773 SourceCollectionIsNotAllowedToNest,774775 776 CollectionFieldSizeExceeded,777778 779 NoSpaceForProperty,780781 782 PropertyLimitReached,783784 785 PropertyKeyIsTooLong,786787 788 InvalidCharacterInPropertyKey,789790 791 EmptyPropertyKey,792793 794 CollectionIsExternal,795796 797 CollectionIsInternal,798799 800 ConfirmSponsorshipFail,801802 803 UserIsNotCollectionAdmin,804 }805806 807 #[pallet::storage]808 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;809810 811 #[pallet::storage]812 pub type DestroyedCollectionCount<T> =813 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;814815 816 #[pallet::storage]817 pub type CollectionById<T> = StorageMap<818 Hasher = Blake2_128Concat,819 Key = CollectionId,820 Value = Collection<<T as frame_system::Config>::AccountId>,821 QueryKind = OptionQuery,822 >;823824 825 #[pallet::storage]826 #[pallet::getter(fn collection_properties)]827 pub type CollectionProperties<T> = StorageMap<828 Hasher = Blake2_128Concat,829 Key = CollectionId,830 Value = CollectionPropertiesT,831 QueryKind = ValueQuery,832 >;833834 835 #[pallet::storage]836 #[pallet::getter(fn property_permissions)]837 pub type CollectionPropertyPermissions<T> = StorageMap<838 Hasher = Blake2_128Concat,839 Key = CollectionId,840 Value = PropertiesPermissionMap,841 QueryKind = ValueQuery,842 >;843844 845 #[pallet::storage]846 pub type AdminAmount<T> = StorageMap<847 Hasher = Blake2_128Concat,848 Key = CollectionId,849 Value = u32,850 QueryKind = ValueQuery,851 >;852853 854 #[pallet::storage]855 pub type IsAdmin<T: Config> = StorageNMap<856 Key = (857 Key<Blake2_128Concat, CollectionId>,858 Key<Blake2_128Concat, T::CrossAccountId>,859 ),860 Value = bool,861 QueryKind = ValueQuery,862 >;863864 865 #[pallet::storage]866 pub type Allowlist<T: Config> = StorageNMap<867 Key = (868 Key<Blake2_128Concat, CollectionId>,869 Key<Blake2_128Concat, T::CrossAccountId>,870 ),871 Value = bool,872 QueryKind = ValueQuery,873 >;874875 876 #[pallet::storage]877 pub type DummyStorageValue<T: Config> = StorageValue<878 Value = (879 CollectionStats,880 CollectionId,881 TokenId,882 TokenChild,883 PhantomType<(884 TokenData<T::CrossAccountId>,885 RpcCollection<T::AccountId>,886 887 PovInfo,888 )>,889 ),890 QueryKind = OptionQuery,891 >;892}893894impl<T: Config> Pallet<T> {895 896 897 898 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {899 ensure!(900 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,901 <Error<T>>::AddressIsZero902 );903 Ok(())904 }905906 907 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {908 <IsAdmin<T>>::iter_prefix((collection,))909 .map(|(a, _)| a)910 .collect()911 }912913 914 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {915 <Allowlist<T>>::iter_prefix((collection,))916 .map(|(a, _)| a)917 .collect()918 }919920 921 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {922 <Allowlist<T>>::get((collection, user))923 }924925 926 pub fn collection_stats() -> CollectionStats {927 let created = <CreatedCollectionCount<T>>::get();928 let destroyed = <DestroyedCollectionCount<T>>::get();929 CollectionStats {930 created: created.0,931 destroyed: destroyed.0,932 alive: created.0 - destroyed.0,933 }934 }935936 937 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {938 let collection = <CollectionById<T>>::get(collection)?;939 let limits = collection.limits;940 let effective_limits = CollectionLimits {941 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),942 sponsored_data_size: Some(limits.sponsored_data_size()),943 sponsored_data_rate_limit: Some(944 limits945 .sponsored_data_rate_limit946 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),947 ),948 token_limit: Some(limits.token_limit()),949 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(950 match collection.mode {951 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,952 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,953 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,954 },955 )),956 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),957 owner_can_transfer: Some(limits.owner_can_transfer()),958 owner_can_destroy: Some(limits.owner_can_destroy()),959 transfers_enabled: Some(limits.transfers_enabled()),960 };961962 Some(effective_limits)963 }964965 966 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {967 let Collection {968 name,969 description,970 owner,971 mode,972 token_prefix,973 sponsorship,974 limits,975 permissions,976 flags,977 } = <CollectionById<T>>::get(collection)?;978979 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)980 .into_iter()981 .map(|(key, permission)| PropertyKeyPermission { key, permission })982 .collect();983984 let properties = <CollectionProperties<T>>::get(collection)985 .into_iter()986 .map(|(key, value)| Property { key, value })987 .collect();988989 let permissions = CollectionPermissions {990 access: Some(permissions.access()),991 mint_mode: Some(permissions.mint_mode()),992 nesting: Some(permissions.nesting().clone()),993 };994995 Some(RpcCollection {996 name: name.into_inner(),997 description: description.into_inner(),998 owner,999 mode,1000 token_prefix: token_prefix.into_inner(),1001 sponsorship,1002 limits,1003 permissions,1004 token_property_permissions,1005 properties,1006 read_only: flags.external,10071008 flags: RpcCollectionFlags {1009 foreign: flags.foreign,1010 erc721metadata: flags.erc721metadata,1011 },1012 })1013 }1014}10151016macro_rules! limit_default {1017 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1018 $(1019 if let Some($new) = $new.$field {1020 let $old = $old.$field($($arg)?);1021 let _ = $new;1022 let _ = $old;1023 $check1024 } else {1025 $new.$field = $old.$field1026 }1027 )*1028 }};1029}1030macro_rules! limit_default_clone {1031 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1032 $(1033 if let Some($new) = $new.$field.clone() {1034 let $old = $old.$field($($arg)?);1035 let _ = $new;1036 let _ = $old;1037 $check1038 } else {1039 $new.$field = $old.$field.clone()1040 }1041 )*1042 }};1043}10441045impl<T: Config> Pallet<T> {1046 1047 1048 1049 1050 1051 pub fn init_collection(1052 owner: T::CrossAccountId,1053 payer: T::CrossAccountId,1054 data: CreateCollectionData<T::AccountId>,1055 flags: CollectionFlags,1056 ) -> Result<CollectionId, DispatchError> {1057 {1058 ensure!(1059 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1060 Error::<T>::CollectionTokenPrefixLimitExceeded1061 );1062 }10631064 let created_count = <CreatedCollectionCount<T>>::get()1065 .01066 .checked_add(1)1067 .ok_or(ArithmeticError::Overflow)?;1068 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1069 let id = CollectionId(created_count);10701071 1072 ensure!(1073 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1074 <Error<T>>::TotalCollectionsLimitExceeded1075 );10761077 10781079 let collection = Collection {1080 owner: owner.as_sub().clone(),1081 name: data.name,1082 mode: data.mode.clone(),1083 description: data.description,1084 token_prefix: data.token_prefix,1085 sponsorship: data1086 .pending_sponsor1087 .map(SponsorshipState::Unconfirmed)1088 .unwrap_or_default(),1089 limits: data1090 .limits1091 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1092 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1093 permissions: data1094 .permissions1095 .map(|permissions| {1096 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1097 })1098 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1099 flags,1100 };11011102 let mut collection_properties = CollectionPropertiesT::new();1103 collection_properties1104 .try_set_from_iter(data.properties.into_iter())1105 .map_err(<Error<T>>::from)?;11061107 CollectionProperties::<T>::insert(id, collection_properties);11081109 let mut token_props_permissions = PropertiesPermissionMap::new();1110 token_props_permissions1111 .try_set_from_iter(data.token_property_permissions.into_iter())1112 .map_err(<Error<T>>::from)?;11131114 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11151116 1117 {1118 let mut imbalance = <Debt<T::AccountId, <T as Config>::Currency>>::zero();1119 imbalance.subsume(<T as Config>::Currency::deposit(1120 &T::TreasuryAccountId::get(),1121 T::CollectionCreationPrice::get(),1122 Precision::Exact,1123 )?);1124 let credit =1125 <T as Config>::Currency::settle(payer.as_sub(), imbalance, Preservation::Preserve)1126 .map_err(|_| Error::<T>::NotSufficientFounds)?;11271128 debug_assert!(credit.peek().is_zero())1129 }11301131 <CreatedCollectionCount<T>>::put(created_count);1132 <Pallet<T>>::deposit_event(Event::CollectionCreated(1133 id,1134 data.mode.id(),1135 owner.as_sub().clone(),1136 ));1137 <PalletEvm<T>>::deposit_log(1138 erc::CollectionHelpersEvents::CollectionCreated {1139 owner: *owner.as_eth(),1140 collection_id: eth::collection_id_to_address(id),1141 }1142 .to_log(T::ContractAddress::get()),1143 );1144 <CollectionById<T>>::insert(id, collection);1145 Ok(id)1146 }11471148 1149 1150 1151 1152 pub fn destroy_collection(1153 collection: CollectionHandle<T>,1154 sender: &T::CrossAccountId,1155 ) -> DispatchResult {1156 ensure!(1157 collection.limits.owner_can_destroy(),1158 <Error<T>>::NoPermission,1159 );1160 collection.check_is_owner(sender)?;11611162 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1163 .01164 .checked_add(1)1165 .ok_or(ArithmeticError::Overflow)?;11661167 11681169 <DestroyedCollectionCount<T>>::put(destroyed_collections);1170 <CollectionById<T>>::remove(collection.id);1171 <AdminAmount<T>>::remove(collection.id);1172 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1173 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1174 <CollectionProperties<T>>::remove(collection.id);11751176 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11771178 <PalletEvm<T>>::deposit_log(1179 erc::CollectionHelpersEvents::CollectionDestroyed {1180 collection_id: eth::collection_id_to_address(collection.id),1181 }1182 .to_log(T::ContractAddress::get()),1183 );1184 Ok(())1185 }11861187 1188 1189 1190 1191 1192 1193 1194 1195 #[transactional]1196 fn modify_collection_properties(1197 collection: &CollectionHandle<T>,1198 sender: &T::CrossAccountId,1199 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1200 ) -> DispatchResult {1201 collection.check_is_owner_or_admin(sender)?;12021203 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12041205 for (key, value) in properties_updates {1206 match value {1207 Some(value) => {1208 stored_properties1209 .try_set(key.clone(), value)1210 .map_err(<Error<T>>::from)?;12111212 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1213 <PalletEvm<T>>::deposit_log(1214 erc::CollectionHelpersEvents::CollectionChanged {1215 collection_id: eth::collection_id_to_address(collection.id),1216 }1217 .to_log(T::ContractAddress::get()),1218 );1219 }1220 None => {1221 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12221223 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1224 <PalletEvm<T>>::deposit_log(1225 erc::CollectionHelpersEvents::CollectionChanged {1226 collection_id: eth::collection_id_to_address(collection.id),1227 }1228 .to_log(T::ContractAddress::get()),1229 );1230 }1231 }1232 }12331234 <CollectionProperties<T>>::set(collection.id, stored_properties);12351236 Ok(())1237 }12381239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 pub fn modify_token_properties(1257 collection: &CollectionHandle<T>,1258 sender: &T::CrossAccountId,1259 token_id: TokenId,1260 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1261 is_token_create: bool,1262 mut stored_properties: TokenProperties,1263 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1264 set_token_properties: impl FnOnce(TokenProperties),1265 log: evm_coder::ethereum::Log,1266 ) -> DispatchResult {1267 let is_collection_admin = collection.is_owner_or_admin(sender);1268 let permissions = Self::property_permissions(collection.id);12691270 let mut token_owner_result = None;1271 let mut is_token_owner = || -> Result<bool, DispatchError> {1272 *token_owner_result.get_or_insert_with(&is_token_owner)1273 };12741275 for (key, value) in properties_updates {1276 let permission = permissions1277 .get(&key)1278 .cloned()1279 .unwrap_or_else(PropertyPermission::none);12801281 let is_property_exists = stored_properties.get(&key).is_some();12821283 match permission {1284 PropertyPermission { mutable: false, .. } if is_property_exists => {1285 return Err(<Error<T>>::NoPermission.into());1286 }12871288 PropertyPermission {1289 collection_admin,1290 token_owner,1291 ..1292 } => {1293 1294 let is_token_create =1295 is_token_create && (collection_admin || token_owner) && value.is_some();1296 if !(is_token_create1297 || (collection_admin && is_collection_admin)1298 || (token_owner && is_token_owner()?))1299 {1300 fail!(<Error<T>>::NoPermission);1301 }1302 }1303 }13041305 match value {1306 Some(value) => {1307 stored_properties1308 .try_set(key.clone(), value)1309 .map_err(<Error<T>>::from)?;13101311 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1312 }1313 None => {1314 stored_properties.remove(&key).map_err(<Error<T>>::from)?;13151316 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1317 }1318 }13191320 <PalletEvm<T>>::deposit_log(log.clone());1321 }13221323 set_token_properties(stored_properties);13241325 Ok(())1326 }13271328 1329 1330 1331 1332 1333 1334 pub fn set_allowance_for_all(1335 collection: &CollectionHandle<T>,1336 owner: &T::CrossAccountId,1337 operator: &T::CrossAccountId,1338 approve: bool,1339 set_allowance: impl FnOnce(),1340 log: evm_coder::ethereum::Log,1341 ) -> DispatchResult {1342 if collection.permissions.access() == AccessMode::AllowList {1343 collection.check_allowlist(owner)?;1344 collection.check_allowlist(operator)?;1345 }13461347 Self::ensure_correct_receiver(operator)?;13481349 set_allowance();13501351 <PalletEvm<T>>::deposit_log(log);1352 Self::deposit_event(Event::ApprovedForAll(1353 collection.id,1354 owner.clone(),1355 operator.clone(),1356 approve,1357 ));1358 Ok(())1359 }13601361 1362 1363 1364 1365 1366 pub fn set_collection_property(1367 collection: &CollectionHandle<T>,1368 sender: &T::CrossAccountId,1369 property: Property,1370 ) -> DispatchResult {1371 Self::set_collection_properties(collection, sender, [property].into_iter())1372 }13731374 1375 1376 1377 1378 1379 1380 pub fn set_scoped_collection_property(1381 collection_id: CollectionId,1382 scope: PropertyScope,1383 property: Property,1384 ) -> DispatchResult {1385 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1386 properties.try_scoped_set(scope, property.key, property.value)1387 })1388 .map_err(<Error<T>>::from)?;13891390 Ok(())1391 }13921393 1394 1395 1396 1397 1398 1399 pub fn set_scoped_collection_properties(1400 collection_id: CollectionId,1401 scope: PropertyScope,1402 properties: impl Iterator<Item = Property>,1403 ) -> DispatchResult {1404 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1405 stored_properties.try_scoped_set_from_iter(scope, properties)1406 })1407 .map_err(<Error<T>>::from)?;14081409 Ok(())1410 }14111412 1413 1414 1415 1416 1417 pub fn set_collection_properties(1418 collection: &CollectionHandle<T>,1419 sender: &T::CrossAccountId,1420 properties: impl Iterator<Item = Property>,1421 ) -> DispatchResult {1422 Self::modify_collection_properties(1423 collection,1424 sender,1425 properties.map(|property| (property.key, Some(property.value))),1426 )1427 }14281429 1430 1431 1432 1433 1434 pub fn delete_collection_property(1435 collection: &CollectionHandle<T>,1436 sender: &T::CrossAccountId,1437 property_key: PropertyKey,1438 ) -> DispatchResult {1439 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1440 }14411442 1443 1444 1445 1446 1447 pub fn delete_collection_properties(1448 collection: &CollectionHandle<T>,1449 sender: &T::CrossAccountId,1450 property_keys: impl Iterator<Item = PropertyKey>,1451 ) -> DispatchResult {1452 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1453 }14541455 1456 1457 1458 1459 1460 1461 pub fn set_property_permission_unchecked(1462 collection: CollectionId,1463 property_permission: PropertyKeyPermission,1464 ) -> DispatchResult {1465 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1466 permissions.try_set(property_permission.key, property_permission.permission)1467 })1468 .map_err(<Error<T>>::from)?;1469 Ok(())1470 }14711472 1473 1474 1475 1476 1477 pub fn set_property_permission(1478 collection: &CollectionHandle<T>,1479 sender: &T::CrossAccountId,1480 property_permission: PropertyKeyPermission,1481 ) -> DispatchResult {1482 Self::set_scoped_property_permission(1483 collection,1484 sender,1485 PropertyScope::None,1486 property_permission,1487 )1488 }14891490 1491 1492 1493 1494 1495 1496 pub fn set_scoped_property_permission(1497 collection: &CollectionHandle<T>,1498 sender: &T::CrossAccountId,1499 scope: PropertyScope,1500 property_permission: PropertyKeyPermission,1501 ) -> DispatchResult {1502 collection.check_is_owner_or_admin(sender)?;15031504 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1505 let current_permission = all_permissions.get(&property_permission.key);1506 if matches![1507 current_permission,1508 Some(PropertyPermission { mutable: false, .. })1509 ] {1510 return Err(<Error<T>>::NoPermission.into());1511 }15121513 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1514 let property_permission = property_permission.clone();1515 permissions.try_scoped_set(1516 scope,1517 property_permission.key,1518 property_permission.permission,1519 )1520 })1521 .map_err(<Error<T>>::from)?;15221523 Self::deposit_event(Event::PropertyPermissionSet(1524 collection.id,1525 property_permission.key,1526 ));1527 <PalletEvm<T>>::deposit_log(1528 erc::CollectionHelpersEvents::CollectionChanged {1529 collection_id: eth::collection_id_to_address(collection.id),1530 }1531 .to_log(T::ContractAddress::get()),1532 );15331534 Ok(())1535 }15361537 1538 1539 1540 1541 1542 #[transactional]1543 pub fn set_token_property_permissions(1544 collection: &CollectionHandle<T>,1545 sender: &T::CrossAccountId,1546 property_permissions: Vec<PropertyKeyPermission>,1547 ) -> DispatchResult {1548 Self::set_scoped_token_property_permissions(1549 collection,1550 sender,1551 PropertyScope::None,1552 property_permissions,1553 )1554 }15551556 1557 1558 1559 1560 1561 1562 #[transactional]1563 pub fn set_scoped_token_property_permissions(1564 collection: &CollectionHandle<T>,1565 sender: &T::CrossAccountId,1566 scope: PropertyScope,1567 property_permissions: Vec<PropertyKeyPermission>,1568 ) -> DispatchResult {1569 for prop_pemission in property_permissions {1570 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1571 }15721573 Ok(())1574 }15751576 1577 pub fn get_collection_property(1578 collection_id: CollectionId,1579 key: &PropertyKey,1580 ) -> Option<PropertyValue> {1581 Self::collection_properties(collection_id).get(key).cloned()1582 }15831584 1585 pub fn bytes_keys_to_property_keys(1586 keys: Vec<Vec<u8>>,1587 ) -> Result<Vec<PropertyKey>, DispatchError> {1588 keys.into_iter()1589 .map(|key| -> Result<PropertyKey, DispatchError> {1590 key.try_into()1591 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1592 })1593 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1594 }15951596 1597 pub fn filter_collection_properties(1598 collection_id: CollectionId,1599 keys: Option<Vec<PropertyKey>>,1600 ) -> Result<Vec<Property>, DispatchError> {1601 let properties = Self::collection_properties(collection_id);16021603 let properties = keys1604 .map(|keys| {1605 keys.into_iter()1606 .filter_map(|key| {1607 properties.get(&key).map(|value| Property {1608 key,1609 value: value.clone(),1610 })1611 })1612 .collect()1613 })1614 .unwrap_or_else(|| {1615 properties1616 .into_iter()1617 .map(|(key, value)| Property { key, value })1618 .collect()1619 });16201621 Ok(properties)1622 }16231624 1625 pub fn filter_property_permissions(1626 collection_id: CollectionId,1627 keys: Option<Vec<PropertyKey>>,1628 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1629 let permissions = Self::property_permissions(collection_id);16301631 let key_permissions = keys1632 .map(|keys| {1633 keys.into_iter()1634 .filter_map(|key| {1635 permissions1636 .get(&key)1637 .map(|permission| PropertyKeyPermission {1638 key,1639 permission: permission.clone(),1640 })1641 })1642 .collect()1643 })1644 .unwrap_or_else(|| {1645 permissions1646 .into_iter()1647 .map(|(key, permission)| PropertyKeyPermission { key, permission })1648 .collect()1649 });16501651 Ok(key_permissions)1652 }16531654 1655 1656 1657 pub fn toggle_allowlist(1658 collection: &CollectionHandle<T>,1659 sender: &T::CrossAccountId,1660 user: &T::CrossAccountId,1661 allowed: bool,1662 ) -> DispatchResult {1663 collection.check_is_owner_or_admin(sender)?;16641665 16661667 if allowed {1668 <Allowlist<T>>::insert((collection.id, user), true);1669 Self::deposit_event(Event::<T>::AllowListAddressAdded(1670 collection.id,1671 user.clone(),1672 ));1673 } else {1674 <Allowlist<T>>::remove((collection.id, user));1675 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1676 collection.id,1677 user.clone(),1678 ));1679 }16801681 <PalletEvm<T>>::deposit_log(1682 erc::CollectionHelpersEvents::CollectionChanged {1683 collection_id: eth::collection_id_to_address(collection.id),1684 }1685 .to_log(T::ContractAddress::get()),1686 );16871688 Ok(())1689 }16901691 1692 1693 1694 pub fn toggle_admin(1695 collection: &CollectionHandle<T>,1696 sender: &T::CrossAccountId,1697 user: &T::CrossAccountId,1698 admin: bool,1699 ) -> DispatchResult {1700 collection.check_is_internal()?;1701 collection.check_is_owner(sender)?;17021703 let is_admin = <IsAdmin<T>>::get((collection.id, user));1704 if is_admin == admin {1705 if admin {1706 return Ok(());1707 } else {1708 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1709 }1710 }1711 let amount = <AdminAmount<T>>::get(collection.id);17121713 17141715 if admin {1716 let amount = amount1717 .checked_add(1)1718 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1719 ensure!(1720 amount <= Self::collection_admins_limit(),1721 <Error<T>>::CollectionAdminCountExceeded,1722 );17231724 <AdminAmount<T>>::insert(collection.id, amount);1725 <IsAdmin<T>>::insert((collection.id, user), true);17261727 Self::deposit_event(Event::<T>::CollectionAdminAdded(1728 collection.id,1729 user.clone(),1730 ));1731 } else {1732 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1733 <IsAdmin<T>>::remove((collection.id, user));17341735 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1736 collection.id,1737 user.clone(),1738 ));1739 }17401741 <PalletEvm<T>>::deposit_log(1742 erc::CollectionHelpersEvents::CollectionChanged {1743 collection_id: eth::collection_id_to_address(collection.id),1744 }1745 .to_log(T::ContractAddress::get()),1746 );17471748 Ok(())1749 }17501751 1752 pub fn update_limits(1753 user: &T::CrossAccountId,1754 collection: &mut CollectionHandle<T>,1755 new_limit: CollectionLimits,1756 ) -> DispatchResult {1757 collection.check_is_internal()?;1758 collection.check_is_owner_or_admin(user)?;17591760 collection.limits =1761 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;17621763 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1764 <PalletEvm<T>>::deposit_log(1765 erc::CollectionHelpersEvents::CollectionChanged {1766 collection_id: eth::collection_id_to_address(collection.id),1767 }1768 .to_log(T::ContractAddress::get()),1769 );17701771 collection.save()1772 }17731774 1775 fn clamp_limits(1776 mode: CollectionMode,1777 old_limit: &CollectionLimits,1778 mut new_limit: CollectionLimits,1779 ) -> Result<CollectionLimits, DispatchError> {1780 let limits = old_limit;1781 limit_default!(old_limit, new_limit,1782 account_token_ownership_limit => ensure!(1783 new_limit <= MAX_TOKEN_OWNERSHIP,1784 <Error<T>>::CollectionLimitBoundsExceeded,1785 ),1786 sponsored_data_size => ensure!(1787 new_limit <= CUSTOM_DATA_LIMIT,1788 <Error<T>>::CollectionLimitBoundsExceeded,1789 ),17901791 sponsored_data_rate_limit => {},1792 token_limit => ensure!(1793 old_limit >= new_limit && new_limit > 0,1794 <Error<T>>::CollectionTokenLimitExceeded1795 ),17961797 sponsor_transfer_timeout(match mode {1798 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1799 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1800 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1801 }) => ensure!(1802 new_limit <= MAX_SPONSOR_TIMEOUT,1803 <Error<T>>::CollectionLimitBoundsExceeded,1804 ),1805 sponsor_approve_timeout => {},1806 owner_can_transfer => ensure!(1807 !limits.owner_can_transfer_instaled() ||1808 old_limit || !new_limit,1809 <Error<T>>::OwnerPermissionsCantBeReverted,1810 ),1811 owner_can_destroy => ensure!(1812 old_limit || !new_limit,1813 <Error<T>>::OwnerPermissionsCantBeReverted,1814 ),1815 transfers_enabled => {},1816 );1817 Ok(new_limit)1818 }18191820 1821 pub fn update_permissions(1822 user: &T::CrossAccountId,1823 collection: &mut CollectionHandle<T>,1824 new_permission: CollectionPermissions,1825 ) -> DispatchResult {1826 collection.check_is_internal()?;1827 collection.check_is_owner_or_admin(user)?;1828 collection.permissions = Self::clamp_permissions(1829 collection.mode.clone(),1830 &collection.permissions,1831 new_permission,1832 )?;18331834 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1835 <PalletEvm<T>>::deposit_log(1836 erc::CollectionHelpersEvents::CollectionChanged {1837 collection_id: eth::collection_id_to_address(collection.id),1838 }1839 .to_log(T::ContractAddress::get()),1840 );18411842 collection.save()1843 }18441845 1846 fn clamp_permissions(1847 _mode: CollectionMode,1848 old_permission: &CollectionPermissions,1849 mut new_permission: CollectionPermissions,1850 ) -> Result<CollectionPermissions, DispatchError> {1851 limit_default_clone!(old_permission, new_permission,1852 access => {},1853 mint_mode => {},1854 nesting => { },1855 );1856 Ok(new_permission)1857 }18581859 1860 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1861 CollectionProperties::<T>::mutate(collection_id, |properties| {1862 properties.recompute_consumed_space();1863 });18641865 Ok(())1866 }1867}186818691870#[macro_export]1871macro_rules! unsupported {1872 ($runtime:path) => {1873 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1874 };1875}187618771878pub trait CommonWeightInfo<CrossAccountId> {1879 1880 fn create_item(data: &CreateItemData) -> Weight {1881 Self::create_multiple_items(from_ref(data))1882 }18831884 1885 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;18861887 1888 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;18891890 1891 fn burn_item() -> Weight;18921893 1894 1895 1896 fn set_collection_properties(amount: u32) -> Weight;18971898 1899 1900 1901 fn delete_collection_properties(amount: u32) -> Weight;19021903 1904 1905 1906 fn set_token_properties(amount: u32) -> Weight;19071908 1909 1910 1911 fn delete_token_properties(amount: u32) -> Weight;19121913 1914 1915 1916 fn set_token_property_permissions(amount: u32) -> Weight;19171918 1919 fn transfer() -> Weight;19201921 1922 fn approve() -> Weight;19231924 1925 fn approve_from() -> Weight;19261927 1928 fn transfer_from() -> Weight;19291930 1931 fn burn_from() -> Weight;19321933 1934 1935 1936 1937 fn burn_recursively_self_raw() -> Weight;19381939 1940 1941 1942 fn burn_recursively_breadth_raw(amount: u32) -> Weight;19431944 1945 1946 1947 1948 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1949 Self::burn_recursively_self_raw()1950 .saturating_mul(max_selfs.max(1) as u64)1951 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1952 }19531954 1955 fn token_owner() -> Weight;19561957 1958 fn set_allowance_for_all() -> Weight;19591960 1961 fn force_repair_item() -> Weight;1962}196319641965pub trait RefungibleExtensionsWeightInfo {1966 1967 fn repartition() -> Weight;1968}196919701971197219731974pub trait CommonCollectionOperations<T: Config> {1975 1976 1977 1978 1979 1980 1981 fn create_item(1982 &self,1983 sender: T::CrossAccountId,1984 to: T::CrossAccountId,1985 data: CreateItemData,1986 nesting_budget: &dyn Budget,1987 ) -> DispatchResultWithPostInfo;19881989 1990 1991 1992 1993 1994 1995 fn create_multiple_items(1996 &self,1997 sender: T::CrossAccountId,1998 to: T::CrossAccountId,1999 data: Vec<CreateItemData>,2000 nesting_budget: &dyn Budget,2001 ) -> DispatchResultWithPostInfo;20022003 2004 2005 2006 2007 2008 2009 fn create_multiple_items_ex(2010 &self,2011 sender: T::CrossAccountId,2012 data: CreateItemExData<T::CrossAccountId>,2013 nesting_budget: &dyn Budget,2014 ) -> DispatchResultWithPostInfo;20152016 2017 2018 2019 2020 2021 fn burn_item(2022 &self,2023 sender: T::CrossAccountId,2024 token: TokenId,2025 amount: u128,2026 ) -> DispatchResultWithPostInfo;20272028 2029 2030 2031 2032 2033 2034 fn burn_item_recursively(2035 &self,2036 sender: T::CrossAccountId,2037 token: TokenId,2038 self_budget: &dyn Budget,2039 breadth_budget: &dyn Budget,2040 ) -> DispatchResultWithPostInfo;20412042 2043 2044 2045 2046 fn set_collection_properties(2047 &self,2048 sender: T::CrossAccountId,2049 properties: Vec<Property>,2050 ) -> DispatchResultWithPostInfo;20512052 2053 2054 2055 2056 fn delete_collection_properties(2057 &self,2058 sender: &T::CrossAccountId,2059 property_keys: Vec<PropertyKey>,2060 ) -> DispatchResultWithPostInfo;20612062 2063 2064 2065 2066 2067 2068 2069 2070 2071 fn set_token_properties(2072 &self,2073 sender: T::CrossAccountId,2074 token_id: TokenId,2075 properties: Vec<Property>,2076 budget: &dyn Budget,2077 ) -> DispatchResultWithPostInfo;20782079 2080 2081 2082 2083 2084 2085 2086 2087 2088 fn delete_token_properties(2089 &self,2090 sender: T::CrossAccountId,2091 token_id: TokenId,2092 property_keys: Vec<PropertyKey>,2093 budget: &dyn Budget,2094 ) -> DispatchResultWithPostInfo;20952096 2097 2098 2099 2100 2101 2102 fn set_token_property_permissions(2103 &self,2104 sender: &T::CrossAccountId,2105 property_permissions: Vec<PropertyKeyPermission>,2106 ) -> DispatchResultWithPostInfo;21072108 2109 2110 2111 2112 2113 2114 2115 fn transfer(2116 &self,2117 sender: T::CrossAccountId,2118 to: T::CrossAccountId,2119 token: TokenId,2120 amount: u128,2121 budget: &dyn Budget,2122 ) -> DispatchResultWithPostInfo;21232124 2125 2126 2127 2128 2129 2130 fn approve(2131 &self,2132 sender: T::CrossAccountId,2133 spender: T::CrossAccountId,2134 token: TokenId,2135 amount: u128,2136 ) -> DispatchResultWithPostInfo;21372138 2139 2140 2141 2142 2143 2144 2145 fn approve_from(2146 &self,2147 sender: T::CrossAccountId,2148 from: T::CrossAccountId,2149 to: T::CrossAccountId,2150 token: TokenId,2151 amount: u128,2152 ) -> DispatchResultWithPostInfo;21532154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 fn transfer_from(2165 &self,2166 sender: T::CrossAccountId,2167 from: T::CrossAccountId,2168 to: T::CrossAccountId,2169 token: TokenId,2170 amount: u128,2171 budget: &dyn Budget,2172 ) -> DispatchResultWithPostInfo;21732174 2175 2176 2177 2178 2179 2180 2181 2182 2183 fn burn_from(2184 &self,2185 sender: T::CrossAccountId,2186 from: T::CrossAccountId,2187 token: TokenId,2188 amount: u128,2189 budget: &dyn Budget,2190 ) -> DispatchResultWithPostInfo;21912192 2193 2194 2195 2196 2197 2198 fn check_nesting(2199 &self,2200 sender: T::CrossAccountId,2201 from: (CollectionId, TokenId),2202 under: TokenId,2203 budget: &dyn Budget,2204 ) -> DispatchResult;22052206 2207 2208 2209 2210 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22112212 2213 2214 2215 2216 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22172218 2219 2220 2221 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;22222223 2224 fn collection_tokens(&self) -> Vec<TokenId>;22252226 2227 2228 2229 fn token_exists(&self, token: TokenId) -> bool;22302231 2232 fn last_token_id(&self) -> TokenId;22332234 2235 2236 2237 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22382239 2240 2241 2242 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22432244 2245 2246 2247 2248 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22492250 2251 2252 2253 2254 2255 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22562257 2258 fn total_supply(&self) -> u32;22592260 2261 2262 2263 fn account_balance(&self, account: T::CrossAccountId) -> u32;22642265 2266 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;22672268 2269 fn total_pieces(&self, token: TokenId) -> Option<u128>;22702271 2272 2273 2274 2275 2276 fn allowance(2277 &self,2278 sender: T::CrossAccountId,2279 spender: T::CrossAccountId,2280 token: TokenId,2281 ) -> u128;22822283 2284 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;22852286 2287 2288 2289 2290 fn set_allowance_for_all(2291 &self,2292 owner: T::CrossAccountId,2293 operator: T::CrossAccountId,2294 approve: bool,2295 ) -> DispatchResultWithPostInfo;22962297 2298 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;22992300 2301 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2302}230323042305pub trait RefungibleExtensions<T>2306where2307 T: Config,2308{2309 2310 2311 2312 2313 2314 2315 2316 fn repartition(2317 &self,2318 sender: &T::CrossAccountId,2319 token: TokenId,2320 amount: u128,2321 ) -> DispatchResultWithPostInfo;2322}23232324232523262327pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2328 let post_info = PostDispatchInfo {2329 actual_weight: Some(weight),2330 pays_fee: Pays::Yes,2331 };2332 match res {2333 Ok(()) => Ok(post_info),2334 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2335 }2336}23372338impl<T: Config> From<PropertiesError> for Error<T> {2339 fn from(error: PropertiesError) -> Self {2340 match error {2341 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2342 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2343 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2344 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2345 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2346 }2347 }2348}