12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#![warn(missing_docs)]53#![cfg_attr(not(feature = "std"), no_std)]54extern crate alloc;5556use core::{57 ops::{Deref, DerefMut},58 slice::from_ref,59};60use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};61use sp_std::vec::Vec;62use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};63use evm_coder::ToLog;64use frame_support::{65 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},66 ensure,67 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},68 dispatch::Pays,69 transactional, fail,70};71use pallet_evm::GasWeightMapping;72use up_data_structs::{73 AccessMode,74 COLLECTION_NUMBER_LIMIT,75 Collection,76 RpcCollection,77 CollectionFlags,78 RpcCollectionFlags,79 CollectionId,80 CreateItemData,81 MAX_TOKEN_PREFIX_LENGTH,82 COLLECTION_ADMINS_LIMIT,83 TokenId,84 TokenChild,85 CollectionStats,86 MAX_TOKEN_OWNERSHIP,87 CollectionMode,88 NFT_SPONSOR_TRANSFER_TIMEOUT,89 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,90 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,91 MAX_SPONSOR_TIMEOUT,92 CUSTOM_DATA_LIMIT,93 CollectionLimits,94 CreateCollectionData,95 SponsorshipState,96 CreateItemExData,97 SponsoringRateLimit,98 budget::Budget,99 PhantomType,100 Property,101 Properties,102 PropertiesPermissionMap,103 PropertyKey,104 PropertyValue,105 PropertyPermission,106 PropertiesError,107 TokenOwnerError,108 PropertyKeyPermission,109 TokenData,110 TrySetProperty,111 PropertyScope,112 CollectionPermissions,113};114use up_pov_estimate_rpc::PovInfo;115116pub use pallet::*;117use sp_core::H160;118use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};119120use crate::erc::CollectionHelpersEvents;121#[cfg(feature = "runtime-benchmarks")]122pub mod benchmarking;123pub mod dispatch;124pub mod erc;125pub mod eth;126pub mod weights;127128129pub type SelfWeightOf<T> = <T as Config>::WeightInfo;130131132133134135136137#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]138pub struct CollectionHandle<T: Config> {139 140 pub id: CollectionId,141 collection: Collection<T::AccountId>,142 143 pub recorder: SubstrateRecorder<T>,144}145146impl<T: Config> WithRecorder<T> for CollectionHandle<T> {147 fn recorder(&self) -> &SubstrateRecorder<T> {148 &self.recorder149 }150 fn into_recorder(self) -> SubstrateRecorder<T> {151 self.recorder152 }153}154155impl<T: Config> CollectionHandle<T> {156 157 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {158 <CollectionById<T>>::get(id).map(|collection| Self {159 id,160 collection,161 recorder: SubstrateRecorder::new(gas_limit),162 })163 }164165 166 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {167 <CollectionById<T>>::get(id).map(|collection| Self {168 id,169 collection,170 recorder,171 })172 }173174 175 176 pub fn new(id: CollectionId) -> Option<Self> {177 Self::new_with_gas_limit(id, u64::MAX)178 }179180 181 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {182 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)183 }184185 186 pub fn consume_store_reads(187 &self,188 reads: u64,189 ) -> pallet_evm_coder_substrate::execution::Result<()> {190 self.recorder191 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(192 <T as frame_system::Config>::DbWeight::get()193 .read194 .saturating_mul(reads),195 )))196 }197198 199 pub fn consume_store_writes(200 &self,201 writes: u64,202 ) -> pallet_evm_coder_substrate::execution::Result<()> {203 self.recorder204 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(205 <T as frame_system::Config>::DbWeight::get()206 .write207 .saturating_mul(writes),208 )))209 }210211 212 pub fn consume_store_reads_and_writes(213 &self,214 reads: u64,215 writes: u64,216 ) -> pallet_evm_coder_substrate::execution::Result<()> {217 let weight = <T as frame_system::Config>::DbWeight::get();218 let reads = weight.read.saturating_mul(reads);219 let writes = weight.read.saturating_mul(writes);220 self.recorder221 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(222 reads.saturating_add(writes),223 )))224 }225226 227 pub fn save(&self) -> DispatchResult {228 <CollectionById<T>>::insert(self.id, &self.collection);229 Ok(())230 }231232 233 234 235 236 237 pub fn set_sponsor(238 &mut self,239 sender: &T::CrossAccountId,240 sponsor: T::AccountId,241 ) -> DispatchResult {242 self.check_is_internal()?;243 self.check_is_owner_or_admin(sender)?;244245 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());246247 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));248 <PalletEvm<T>>::deposit_log(249 erc::CollectionHelpersEvents::CollectionChanged {250 collection_id: eth::collection_id_to_address(self.id),251 }252 .to_log(T::ContractAddress::get()),253 );254255 self.save()256 }257258 259 260 261 262 263 264 265 266 267 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {268 self.check_is_internal()?;269270 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());271272 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));273 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));274 <PalletEvm<T>>::deposit_log(275 erc::CollectionHelpersEvents::CollectionChanged {276 collection_id: eth::collection_id_to_address(self.id),277 }278 .to_log(T::ContractAddress::get()),279 );280281 self.save()282 }283284 285 286 287 288 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {289 self.check_is_internal()?;290 ensure!(291 self.collection.sponsorship.pending_sponsor() == Some(sender),292 Error::<T>::ConfirmSponsorshipFail293 );294295 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());296297 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));298 <PalletEvm<T>>::deposit_log(299 erc::CollectionHelpersEvents::CollectionChanged {300 collection_id: eth::collection_id_to_address(self.id),301 }302 .to_log(T::ContractAddress::get()),303 );304305 self.save()306 }307308 309 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {310 self.check_is_internal()?;311 self.check_is_owner_or_admin(sender)?;312313 self.collection.sponsorship = SponsorshipState::Disabled;314315 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));316 <PalletEvm<T>>::deposit_log(317 erc::CollectionHelpersEvents::CollectionChanged {318 collection_id: eth::collection_id_to_address(self.id),319 }320 .to_log(T::ContractAddress::get()),321 );322 self.save()323 }324325 326 327 328 329 pub fn force_remove_sponsor(&mut self) -> DispatchResult {330 self.check_is_internal()?;331332 self.collection.sponsorship = SponsorshipState::Disabled;333334 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));335 <PalletEvm<T>>::deposit_log(336 erc::CollectionHelpersEvents::CollectionChanged {337 collection_id: eth::collection_id_to_address(self.id),338 }339 .to_log(T::ContractAddress::get()),340 );341 self.save()342 }343344 345 346 pub fn check_is_internal(&self) -> DispatchResult {347 if self.flags.external {348 return Err(<Error<T>>::CollectionIsExternal)?;349 }350351 Ok(())352 }353354 355 356 pub fn check_is_external(&self) -> DispatchResult {357 if !self.flags.external {358 return Err(<Error<T>>::CollectionIsInternal)?;359 }360361 Ok(())362 }363}364365impl<T: Config> Deref for CollectionHandle<T> {366 type Target = Collection<T::AccountId>;367368 fn deref(&self) -> &Self::Target {369 &self.collection370 }371}372373impl<T: Config> DerefMut for CollectionHandle<T> {374 fn deref_mut(&mut self) -> &mut Self::Target {375 &mut self.collection376 }377}378379impl<T: Config> CollectionHandle<T> {380 381 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {382 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);383 Ok(())384 }385386 387 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {388 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))389 }390391 392 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {393 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);394 Ok(())395 }396397 398 399 400 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {401 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)402 }403404 405 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {406 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)407 }408409 410 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {411 ensure!(412 <Allowlist<T>>::get((self.id, user)),413 <Error<T>>::AddressNotInAllowlist414 );415 Ok(())416 }417418 419 420 421 pub fn change_owner(422 &mut self,423 caller: T::CrossAccountId,424 new_owner: T::CrossAccountId,425 ) -> DispatchResult {426 self.check_is_internal()?;427 self.check_is_owner(&caller)?;428 self.collection.owner = new_owner.as_sub().clone();429430 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(431 self.id,432 new_owner.as_sub().clone(),433 ));434 <PalletEvm<T>>::deposit_log(435 erc::CollectionHelpersEvents::CollectionChanged {436 collection_id: eth::collection_id_to_address(self.id),437 }438 .to_log(T::ContractAddress::get()),439 );440441 self.save()442 }443}444445#[frame_support::pallet]446pub mod pallet {447 use super::*;448 use dispatch::CollectionDispatch;449 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};450 use frame_system::pallet_prelude::*;451 use frame_support::traits::Currency;452 use up_data_structs::{TokenId, mapping::TokenAddressMapping};453 use scale_info::TypeInfo;454 use weights::WeightInfo;455456 #[pallet::config]457 pub trait Config:458 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo459 {460 461 type WeightInfo: WeightInfo;462463 464 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;465466 467 type Currency: Currency<Self::AccountId>;468469 470 #[pallet::constant]471 type CollectionCreationPrice: Get<472 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,473 >;474475 476 type CollectionDispatch: CollectionDispatch<Self>;477478 479 type TreasuryAccountId: Get<Self::AccountId>;480481 482 #[pallet::constant]483 type ContractAddress: Get<H160>;484485 486 type EvmTokenAddressMapping: TokenAddressMapping<H160>;487488 489 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;490 }491492 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);493494 #[pallet::pallet]495 #[pallet::storage_version(STORAGE_VERSION)]496 #[pallet::generate_store(pub(super) trait Store)]497 pub struct Pallet<T>(_);498499 #[pallet::extra_constants]500 impl<T: Config> Pallet<T> {501 502 pub fn collection_admins_limit() -> u32 {503 COLLECTION_ADMINS_LIMIT504 }505 }506507 impl<T: Config> Pallet<T> {508 509 pub fn deposit_event(event: Event<T>) {510 let event = <T as Config>::RuntimeEvent::from(event);511 let event = event.into();512 <frame_system::Pallet<T>>::deposit_event(event)513 }514 }515516 #[pallet::event]517 pub enum Event<T: Config> {518 519 CollectionCreated(520 521 CollectionId,522 523 u8,524 525 T::AccountId,526 ),527528 529 CollectionDestroyed(530 531 CollectionId,532 ),533534 535 ItemCreated(536 537 CollectionId,538 539 TokenId,540 541 T::CrossAccountId,542 543 u128,544 ),545546 547 ItemDestroyed(548 549 CollectionId,550 551 TokenId,552 553 T::CrossAccountId,554 555 u128,556 ),557558 559 Transfer(560 561 CollectionId,562 563 TokenId,564 565 T::CrossAccountId,566 567 T::CrossAccountId,568 569 u128,570 ),571572 573 Approved(574 575 CollectionId,576 577 TokenId,578 579 T::CrossAccountId,580 581 T::CrossAccountId,582 583 u128,584 ),585586 587 ApprovedForAll(588 589 CollectionId,590 591 T::CrossAccountId,592 593 T::CrossAccountId,594 595 bool,596 ),597598 599 CollectionPropertySet(600 601 CollectionId,602 603 PropertyKey,604 ),605606 607 CollectionPropertyDeleted(608 609 CollectionId,610 611 PropertyKey,612 ),613614 615 TokenPropertySet(616 617 CollectionId,618 619 TokenId,620 621 PropertyKey,622 ),623624 625 TokenPropertyDeleted(626 627 CollectionId,628 629 TokenId,630 631 PropertyKey,632 ),633634 635 PropertyPermissionSet(636 637 CollectionId,638 639 PropertyKey,640 ),641642 643 AllowListAddressAdded(644 645 CollectionId,646 647 T::CrossAccountId,648 ),649650 651 AllowListAddressRemoved(652 653 CollectionId,654 655 T::CrossAccountId,656 ),657658 659 CollectionAdminAdded(660 661 CollectionId,662 663 T::CrossAccountId,664 ),665666 667 CollectionAdminRemoved(668 669 CollectionId,670 671 T::CrossAccountId,672 ),673674 675 CollectionLimitSet(676 677 CollectionId,678 ),679680 681 CollectionOwnerChanged(682 683 CollectionId,684 685 T::AccountId,686 ),687688 689 CollectionPermissionSet(690 691 CollectionId,692 ),693694 695 CollectionSponsorSet(696 697 CollectionId,698 699 T::AccountId,700 ),701702 703 SponsorshipConfirmed(704 705 CollectionId,706 707 T::AccountId,708 ),709710 711 CollectionSponsorRemoved(712 713 CollectionId,714 ),715 }716717 #[pallet::error]718 pub enum Error<T> {719 720 CollectionNotFound,721 722 MustBeTokenOwner,723 724 NoPermission,725 726 CantDestroyNotEmptyCollection,727 728 PublicMintingNotAllowed,729 730 AddressNotInAllowlist,731732 733 CollectionNameLimitExceeded,734 735 CollectionDescriptionLimitExceeded,736 737 CollectionTokenPrefixLimitExceeded,738 739 TotalCollectionsLimitExceeded,740 741 CollectionAdminCountExceeded,742 743 CollectionLimitBoundsExceeded,744 745 OwnerPermissionsCantBeReverted,746 747 TransferNotAllowed,748 749 AccountTokenLimitExceeded,750 751 CollectionTokenLimitExceeded,752 753 MetadataFlagFrozen,754755 756 TokenNotFound,757 758 TokenValueTooLow,759 760 ApprovedValueTooLow,761 762 CantApproveMoreThanOwned,763 764 AddressIsNotEthMirror,765766 767 AddressIsZero,768769 770 UnsupportedOperation,771772 773 NotSufficientFounds,774775 776 UserIsNotAllowedToNest,777 778 SourceCollectionIsNotAllowedToNest,779780 781 CollectionFieldSizeExceeded,782783 784 NoSpaceForProperty,785786 787 PropertyLimitReached,788789 790 PropertyKeyIsTooLong,791792 793 InvalidCharacterInPropertyKey,794795 796 EmptyPropertyKey,797798 799 CollectionIsExternal,800801 802 CollectionIsInternal,803804 805 ConfirmSponsorshipFail,806807 808 UserIsNotCollectionAdmin,809 }810811 812 #[pallet::storage]813 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;814815 816 #[pallet::storage]817 pub type DestroyedCollectionCount<T> =818 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;819820 821 #[pallet::storage]822 pub type CollectionById<T> = StorageMap<823 Hasher = Blake2_128Concat,824 Key = CollectionId,825 Value = Collection<<T as frame_system::Config>::AccountId>,826 QueryKind = OptionQuery,827 >;828829 830 #[pallet::storage]831 #[pallet::getter(fn collection_properties)]832 pub type CollectionProperties<T> = StorageMap<833 Hasher = Blake2_128Concat,834 Key = CollectionId,835 Value = Properties,836 QueryKind = ValueQuery,837 OnEmpty = up_data_structs::CollectionProperties,838 >;839840 841 #[pallet::storage]842 #[pallet::getter(fn property_permissions)]843 pub type CollectionPropertyPermissions<T> = StorageMap<844 Hasher = Blake2_128Concat,845 Key = CollectionId,846 Value = PropertiesPermissionMap,847 QueryKind = ValueQuery,848 >;849850 851 #[pallet::storage]852 pub type AdminAmount<T> = StorageMap<853 Hasher = Blake2_128Concat,854 Key = CollectionId,855 Value = u32,856 QueryKind = ValueQuery,857 >;858859 860 #[pallet::storage]861 pub type IsAdmin<T: Config> = StorageNMap<862 Key = (863 Key<Blake2_128Concat, CollectionId>,864 Key<Blake2_128Concat, T::CrossAccountId>,865 ),866 Value = bool,867 QueryKind = ValueQuery,868 >;869870 871 #[pallet::storage]872 pub type Allowlist<T: Config> = StorageNMap<873 Key = (874 Key<Blake2_128Concat, CollectionId>,875 Key<Blake2_128Concat, T::CrossAccountId>,876 ),877 Value = bool,878 QueryKind = ValueQuery,879 >;880881 882 #[pallet::storage]883 pub type DummyStorageValue<T: Config> = StorageValue<884 Value = (885 CollectionStats,886 CollectionId,887 TokenId,888 TokenChild,889 PhantomType<(890 TokenData<T::CrossAccountId>,891 RpcCollection<T::AccountId>,892 893 PovInfo,894 )>,895 ),896 QueryKind = OptionQuery,897 >;898899 #[pallet::hooks]900 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {901 fn on_runtime_upgrade() -> Weight {902 StorageVersion::new(1).put::<Pallet<T>>();903904 Weight::zero()905 }906 }907}908909impl<T: Config> Pallet<T> {910 911 912 913 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {914 ensure!(915 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,916 <Error<T>>::AddressIsZero917 );918 Ok(())919 }920921 922 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {923 <IsAdmin<T>>::iter_prefix((collection,))924 .map(|(a, _)| a)925 .collect()926 }927928 929 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {930 <Allowlist<T>>::iter_prefix((collection,))931 .map(|(a, _)| a)932 .collect()933 }934935 936 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {937 <Allowlist<T>>::get((collection, user))938 }939940 941 pub fn collection_stats() -> CollectionStats {942 let created = <CreatedCollectionCount<T>>::get();943 let destroyed = <DestroyedCollectionCount<T>>::get();944 CollectionStats {945 created: created.0,946 destroyed: destroyed.0,947 alive: created.0 - destroyed.0,948 }949 }950951 952 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {953 let collection = <CollectionById<T>>::get(collection)?;954 let limits = collection.limits;955 let effective_limits = CollectionLimits {956 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),957 sponsored_data_size: Some(limits.sponsored_data_size()),958 sponsored_data_rate_limit: Some(959 limits960 .sponsored_data_rate_limit961 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),962 ),963 token_limit: Some(limits.token_limit()),964 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(965 match collection.mode {966 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,967 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,968 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,969 },970 )),971 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),972 owner_can_transfer: Some(limits.owner_can_transfer()),973 owner_can_destroy: Some(limits.owner_can_destroy()),974 transfers_enabled: Some(limits.transfers_enabled()),975 };976977 Some(effective_limits)978 }979980 981 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {982 let Collection {983 name,984 description,985 owner,986 mode,987 token_prefix,988 sponsorship,989 limits,990 permissions,991 flags,992 } = <CollectionById<T>>::get(collection)?;993994 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)995 .into_iter()996 .map(|(key, permission)| PropertyKeyPermission { key, permission })997 .collect();998999 let properties = <CollectionProperties<T>>::get(collection)1000 .into_iter()1001 .map(|(key, value)| Property { key, value })1002 .collect();10031004 let permissions = CollectionPermissions {1005 access: Some(permissions.access()),1006 mint_mode: Some(permissions.mint_mode()),1007 nesting: Some(permissions.nesting().clone()),1008 };10091010 Some(RpcCollection {1011 name: name.into_inner(),1012 description: description.into_inner(),1013 owner,1014 mode,1015 token_prefix: token_prefix.into_inner(),1016 sponsorship,1017 limits,1018 permissions,1019 token_property_permissions,1020 properties,1021 read_only: flags.external,10221023 flags: RpcCollectionFlags {1024 foreign: flags.foreign,1025 erc721metadata: flags.erc721metadata,1026 },1027 })1028 }1029}10301031macro_rules! limit_default {1032 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1033 $(1034 if let Some($new) = $new.$field {1035 let $old = $old.$field($($arg)?);1036 let _ = $new;1037 let _ = $old;1038 $check1039 } else {1040 $new.$field = $old.$field1041 }1042 )*1043 }};1044}1045macro_rules! limit_default_clone {1046 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1047 $(1048 if let Some($new) = $new.$field.clone() {1049 let $old = $old.$field($($arg)?);1050 let _ = $new;1051 let _ = $old;1052 $check1053 } else {1054 $new.$field = $old.$field.clone()1055 }1056 )*1057 }};1058}10591060impl<T: Config> Pallet<T> {1061 1062 1063 1064 1065 1066 pub fn init_collection(1067 owner: T::CrossAccountId,1068 payer: T::CrossAccountId,1069 data: CreateCollectionData<T::AccountId>,1070 flags: CollectionFlags,1071 ) -> Result<CollectionId, DispatchError> {1072 {1073 ensure!(1074 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1075 Error::<T>::CollectionTokenPrefixLimitExceeded1076 );1077 }10781079 let created_count = <CreatedCollectionCount<T>>::get()1080 .01081 .checked_add(1)1082 .ok_or(ArithmeticError::Overflow)?;1083 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1084 let id = CollectionId(created_count);10851086 1087 ensure!(1088 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1089 <Error<T>>::TotalCollectionsLimitExceeded1090 );10911092 10931094 let collection = Collection {1095 owner: owner.as_sub().clone(),1096 name: data.name,1097 mode: data.mode.clone(),1098 description: data.description,1099 token_prefix: data.token_prefix,1100 sponsorship: data1101 .pending_sponsor1102 .map(SponsorshipState::Unconfirmed)1103 .unwrap_or_default(),1104 limits: data1105 .limits1106 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1107 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1108 permissions: data1109 .permissions1110 .map(|permissions| {1111 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1112 })1113 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1114 flags,1115 };11161117 let mut collection_properties = up_data_structs::CollectionProperties::get();1118 collection_properties1119 .try_set_from_iter(data.properties.into_iter())1120 .map_err(<Error<T>>::from)?;11211122 CollectionProperties::<T>::insert(id, collection_properties);11231124 let mut token_props_permissions = PropertiesPermissionMap::new();1125 token_props_permissions1126 .try_set_from_iter(data.token_property_permissions.into_iter())1127 .map_err(<Error<T>>::from)?;11281129 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11301131 1132 {1133 let mut imbalance =1134 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1135 imbalance.subsume(1136 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1137 &T::TreasuryAccountId::get(),1138 T::CollectionCreationPrice::get(),1139 ),1140 );1141 <T as Config>::Currency::settle(1142 payer.as_sub(),1143 imbalance,1144 WithdrawReasons::TRANSFER,1145 ExistenceRequirement::KeepAlive,1146 )1147 .map_err(|_| Error::<T>::NotSufficientFounds)?;1148 }11491150 <CreatedCollectionCount<T>>::put(created_count);1151 <Pallet<T>>::deposit_event(Event::CollectionCreated(1152 id,1153 data.mode.id(),1154 owner.as_sub().clone(),1155 ));1156 <PalletEvm<T>>::deposit_log(1157 erc::CollectionHelpersEvents::CollectionCreated {1158 owner: *owner.as_eth(),1159 collection_id: eth::collection_id_to_address(id),1160 }1161 .to_log(T::ContractAddress::get()),1162 );1163 <CollectionById<T>>::insert(id, collection);1164 Ok(id)1165 }11661167 1168 1169 1170 1171 pub fn destroy_collection(1172 collection: CollectionHandle<T>,1173 sender: &T::CrossAccountId,1174 ) -> DispatchResult {1175 ensure!(1176 collection.limits.owner_can_destroy(),1177 <Error<T>>::NoPermission,1178 );1179 collection.check_is_owner(sender)?;11801181 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1182 .01183 .checked_add(1)1184 .ok_or(ArithmeticError::Overflow)?;11851186 11871188 <DestroyedCollectionCount<T>>::put(destroyed_collections);1189 <CollectionById<T>>::remove(collection.id);1190 <AdminAmount<T>>::remove(collection.id);1191 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1192 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1193 <CollectionProperties<T>>::remove(collection.id);11941195 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11961197 <PalletEvm<T>>::deposit_log(1198 erc::CollectionHelpersEvents::CollectionDestroyed {1199 collection_id: eth::collection_id_to_address(collection.id),1200 }1201 .to_log(T::ContractAddress::get()),1202 );1203 Ok(())1204 }12051206 1207 1208 1209 1210 1211 1212 1213 1214 #[transactional]1215 fn modify_collection_properties(1216 collection: &CollectionHandle<T>,1217 sender: &T::CrossAccountId,1218 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1219 ) -> DispatchResult {1220 collection.check_is_owner_or_admin(sender)?;12211222 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12231224 for (key, value) in properties_updates {1225 match value {1226 Some(value) => {1227 stored_properties1228 .try_set(key.clone(), value)1229 .map_err(<Error<T>>::from)?;12301231 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1232 <PalletEvm<T>>::deposit_log(1233 erc::CollectionHelpersEvents::CollectionChanged {1234 collection_id: eth::collection_id_to_address(collection.id),1235 }1236 .to_log(T::ContractAddress::get()),1237 );1238 }1239 None => {1240 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12411242 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1243 <PalletEvm<T>>::deposit_log(1244 erc::CollectionHelpersEvents::CollectionChanged {1245 collection_id: eth::collection_id_to_address(collection.id),1246 }1247 .to_log(T::ContractAddress::get()),1248 );1249 }1250 }1251 }12521253 <CollectionProperties<T>>::set(collection.id, stored_properties);12541255 Ok(())1256 }12571258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 pub fn modify_token_properties(1276 collection: &CollectionHandle<T>,1277 sender: &T::CrossAccountId,1278 token_id: TokenId,1279 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1280 is_token_create: bool,1281 mut stored_properties: Properties,1282 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1283 set_token_properties: impl FnOnce(Properties),1284 ) -> DispatchResult {1285 let is_collection_admin = collection.is_owner_or_admin(sender);1286 let permissions = Self::property_permissions(collection.id);12871288 let mut token_owner_result = None;1289 let mut is_token_owner = || -> Result<bool, DispatchError> {1290 *token_owner_result.get_or_insert_with(&is_token_owner)1291 };12921293 for (key, value) in properties_updates {1294 let permission = permissions1295 .get(&key)1296 .cloned()1297 .unwrap_or_else(PropertyPermission::none);12981299 let is_property_exists = stored_properties.get(&key).is_some();13001301 match permission {1302 PropertyPermission { mutable: false, .. } if is_property_exists => {1303 return Err(<Error<T>>::NoPermission.into());1304 }13051306 PropertyPermission {1307 collection_admin,1308 token_owner,1309 ..1310 } => {1311 1312 let is_token_create =1313 is_token_create && (collection_admin || token_owner) && value.is_some();1314 if !(is_token_create1315 || (collection_admin && is_collection_admin)1316 || (token_owner && is_token_owner()?))1317 {1318 fail!(<Error<T>>::NoPermission);1319 }1320 }1321 }13221323 match value {1324 Some(value) => {1325 stored_properties1326 .try_set(key.clone(), value)1327 .map_err(<Error<T>>::from)?;13281329 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1330 }1331 None => {1332 stored_properties.remove(&key).map_err(<Error<T>>::from)?;13331334 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1335 }1336 }13371338 <PalletEvm<T>>::deposit_log(1339 CollectionHelpersEvents::TokenChanged {1340 collection_id: eth::collection_id_to_address(collection.id),1341 token_id: token_id.into(),1342 }1343 .to_log(T::ContractAddress::get()),1344 );1345 }13461347 set_token_properties(stored_properties);13481349 Ok(())1350 }13511352 1353 1354 1355 1356 1357 1358 pub fn set_allowance_for_all(1359 collection: &CollectionHandle<T>,1360 owner: &T::CrossAccountId,1361 operator: &T::CrossAccountId,1362 approve: bool,1363 set_allowance: impl FnOnce(),1364 log: evm_coder::ethereum::Log,1365 ) -> DispatchResult {1366 if collection.permissions.access() == AccessMode::AllowList {1367 collection.check_allowlist(owner)?;1368 collection.check_allowlist(operator)?;1369 }13701371 Self::ensure_correct_receiver(operator)?;13721373 set_allowance();13741375 <PalletEvm<T>>::deposit_log(log);1376 Self::deposit_event(Event::ApprovedForAll(1377 collection.id,1378 owner.clone(),1379 operator.clone(),1380 approve,1381 ));1382 Ok(())1383 }13841385 1386 1387 1388 1389 1390 pub fn set_collection_property(1391 collection: &CollectionHandle<T>,1392 sender: &T::CrossAccountId,1393 property: Property,1394 ) -> DispatchResult {1395 Self::set_collection_properties(collection, sender, [property].into_iter())1396 }13971398 1399 1400 1401 1402 1403 1404 pub fn set_scoped_collection_property(1405 collection_id: CollectionId,1406 scope: PropertyScope,1407 property: Property,1408 ) -> DispatchResult {1409 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1410 properties.try_scoped_set(scope, property.key, property.value)1411 })1412 .map_err(<Error<T>>::from)?;14131414 Ok(())1415 }14161417 1418 1419 1420 1421 1422 1423 pub fn set_scoped_collection_properties(1424 collection_id: CollectionId,1425 scope: PropertyScope,1426 properties: impl Iterator<Item = Property>,1427 ) -> DispatchResult {1428 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1429 stored_properties.try_scoped_set_from_iter(scope, properties)1430 })1431 .map_err(<Error<T>>::from)?;14321433 Ok(())1434 }14351436 1437 1438 1439 1440 1441 pub fn set_collection_properties(1442 collection: &CollectionHandle<T>,1443 sender: &T::CrossAccountId,1444 properties: impl Iterator<Item = Property>,1445 ) -> DispatchResult {1446 Self::modify_collection_properties(1447 collection,1448 sender,1449 properties.map(|property| (property.key, Some(property.value))),1450 )1451 }14521453 1454 1455 1456 1457 1458 pub fn delete_collection_property(1459 collection: &CollectionHandle<T>,1460 sender: &T::CrossAccountId,1461 property_key: PropertyKey,1462 ) -> DispatchResult {1463 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1464 }14651466 1467 1468 1469 1470 1471 pub fn delete_collection_properties(1472 collection: &CollectionHandle<T>,1473 sender: &T::CrossAccountId,1474 property_keys: impl Iterator<Item = PropertyKey>,1475 ) -> DispatchResult {1476 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1477 }14781479 1480 1481 1482 1483 1484 1485 pub fn set_property_permission_unchecked(1486 collection: CollectionId,1487 property_permission: PropertyKeyPermission,1488 ) -> DispatchResult {1489 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1490 permissions.try_set(property_permission.key, property_permission.permission)1491 })1492 .map_err(<Error<T>>::from)?;1493 Ok(())1494 }14951496 1497 1498 1499 1500 1501 pub fn set_property_permission(1502 collection: &CollectionHandle<T>,1503 sender: &T::CrossAccountId,1504 property_permission: PropertyKeyPermission,1505 ) -> DispatchResult {1506 Self::set_scoped_property_permission(1507 collection,1508 sender,1509 PropertyScope::None,1510 property_permission,1511 )1512 }15131514 1515 1516 1517 1518 1519 1520 pub fn set_scoped_property_permission(1521 collection: &CollectionHandle<T>,1522 sender: &T::CrossAccountId,1523 scope: PropertyScope,1524 property_permission: PropertyKeyPermission,1525 ) -> DispatchResult {1526 collection.check_is_owner_or_admin(sender)?;15271528 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1529 let current_permission = all_permissions.get(&property_permission.key);1530 if matches![1531 current_permission,1532 Some(PropertyPermission { mutable: false, .. })1533 ] {1534 return Err(<Error<T>>::NoPermission.into());1535 }15361537 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1538 let property_permission = property_permission.clone();1539 permissions.try_scoped_set(1540 scope,1541 property_permission.key,1542 property_permission.permission,1543 )1544 })1545 .map_err(<Error<T>>::from)?;15461547 Self::deposit_event(Event::PropertyPermissionSet(1548 collection.id,1549 property_permission.key,1550 ));1551 <PalletEvm<T>>::deposit_log(1552 erc::CollectionHelpersEvents::CollectionChanged {1553 collection_id: eth::collection_id_to_address(collection.id),1554 }1555 .to_log(T::ContractAddress::get()),1556 );15571558 Ok(())1559 }15601561 1562 1563 1564 1565 1566 #[transactional]1567 pub fn set_token_property_permissions(1568 collection: &CollectionHandle<T>,1569 sender: &T::CrossAccountId,1570 property_permissions: Vec<PropertyKeyPermission>,1571 ) -> DispatchResult {1572 Self::set_scoped_token_property_permissions(1573 collection,1574 sender,1575 PropertyScope::None,1576 property_permissions,1577 )1578 }15791580 1581 1582 1583 1584 1585 1586 #[transactional]1587 pub fn set_scoped_token_property_permissions(1588 collection: &CollectionHandle<T>,1589 sender: &T::CrossAccountId,1590 scope: PropertyScope,1591 property_permissions: Vec<PropertyKeyPermission>,1592 ) -> DispatchResult {1593 for prop_pemission in property_permissions {1594 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1595 }15961597 Ok(())1598 }15991600 1601 pub fn get_collection_property(1602 collection_id: CollectionId,1603 key: &PropertyKey,1604 ) -> Option<PropertyValue> {1605 Self::collection_properties(collection_id).get(key).cloned()1606 }16071608 1609 pub fn bytes_keys_to_property_keys(1610 keys: Vec<Vec<u8>>,1611 ) -> Result<Vec<PropertyKey>, DispatchError> {1612 keys.into_iter()1613 .map(|key| -> Result<PropertyKey, DispatchError> {1614 key.try_into()1615 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1616 })1617 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1618 }16191620 1621 pub fn filter_collection_properties(1622 collection_id: CollectionId,1623 keys: Option<Vec<PropertyKey>>,1624 ) -> Result<Vec<Property>, DispatchError> {1625 let properties = Self::collection_properties(collection_id);16261627 let properties = keys1628 .map(|keys| {1629 keys.into_iter()1630 .filter_map(|key| {1631 properties.get(&key).map(|value| Property {1632 key,1633 value: value.clone(),1634 })1635 })1636 .collect()1637 })1638 .unwrap_or_else(|| {1639 properties1640 .into_iter()1641 .map(|(key, value)| Property { key, value })1642 .collect()1643 });16441645 Ok(properties)1646 }16471648 1649 pub fn filter_property_permissions(1650 collection_id: CollectionId,1651 keys: Option<Vec<PropertyKey>>,1652 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1653 let permissions = Self::property_permissions(collection_id);16541655 let key_permissions = keys1656 .map(|keys| {1657 keys.into_iter()1658 .filter_map(|key| {1659 permissions1660 .get(&key)1661 .map(|permission| PropertyKeyPermission {1662 key,1663 permission: permission.clone(),1664 })1665 })1666 .collect()1667 })1668 .unwrap_or_else(|| {1669 permissions1670 .into_iter()1671 .map(|(key, permission)| PropertyKeyPermission { key, permission })1672 .collect()1673 });16741675 Ok(key_permissions)1676 }16771678 1679 1680 1681 pub fn toggle_allowlist(1682 collection: &CollectionHandle<T>,1683 sender: &T::CrossAccountId,1684 user: &T::CrossAccountId,1685 allowed: bool,1686 ) -> DispatchResult {1687 collection.check_is_owner_or_admin(sender)?;16881689 16901691 if allowed {1692 <Allowlist<T>>::insert((collection.id, user), true);1693 Self::deposit_event(Event::<T>::AllowListAddressAdded(1694 collection.id,1695 user.clone(),1696 ));1697 } else {1698 <Allowlist<T>>::remove((collection.id, user));1699 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1700 collection.id,1701 user.clone(),1702 ));1703 }17041705 <PalletEvm<T>>::deposit_log(1706 erc::CollectionHelpersEvents::CollectionChanged {1707 collection_id: eth::collection_id_to_address(collection.id),1708 }1709 .to_log(T::ContractAddress::get()),1710 );17111712 Ok(())1713 }17141715 1716 1717 1718 pub fn toggle_admin(1719 collection: &CollectionHandle<T>,1720 sender: &T::CrossAccountId,1721 user: &T::CrossAccountId,1722 admin: bool,1723 ) -> DispatchResult {1724 collection.check_is_internal()?;1725 collection.check_is_owner(sender)?;17261727 let is_admin = <IsAdmin<T>>::get((collection.id, user));1728 if is_admin == admin {1729 if admin {1730 return Ok(());1731 } else {1732 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1733 }1734 }1735 let amount = <AdminAmount<T>>::get(collection.id);17361737 17381739 if admin {1740 let amount = amount1741 .checked_add(1)1742 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1743 ensure!(1744 amount <= Self::collection_admins_limit(),1745 <Error<T>>::CollectionAdminCountExceeded,1746 );17471748 <AdminAmount<T>>::insert(collection.id, amount);1749 <IsAdmin<T>>::insert((collection.id, user), true);17501751 Self::deposit_event(Event::<T>::CollectionAdminAdded(1752 collection.id,1753 user.clone(),1754 ));1755 } else {1756 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1757 <IsAdmin<T>>::remove((collection.id, user));17581759 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1760 collection.id,1761 user.clone(),1762 ));1763 }17641765 <PalletEvm<T>>::deposit_log(1766 erc::CollectionHelpersEvents::CollectionChanged {1767 collection_id: eth::collection_id_to_address(collection.id),1768 }1769 .to_log(T::ContractAddress::get()),1770 );17711772 Ok(())1773 }17741775 1776 pub fn update_limits(1777 user: &T::CrossAccountId,1778 collection: &mut CollectionHandle<T>,1779 new_limit: CollectionLimits,1780 ) -> DispatchResult {1781 collection.check_is_internal()?;1782 collection.check_is_owner_or_admin(user)?;17831784 collection.limits =1785 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;17861787 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1788 <PalletEvm<T>>::deposit_log(1789 erc::CollectionHelpersEvents::CollectionChanged {1790 collection_id: eth::collection_id_to_address(collection.id),1791 }1792 .to_log(T::ContractAddress::get()),1793 );17941795 collection.save()1796 }17971798 1799 fn clamp_limits(1800 mode: CollectionMode,1801 old_limit: &CollectionLimits,1802 mut new_limit: CollectionLimits,1803 ) -> Result<CollectionLimits, DispatchError> {1804 let limits = old_limit;1805 limit_default!(old_limit, new_limit,1806 account_token_ownership_limit => ensure!(1807 new_limit <= MAX_TOKEN_OWNERSHIP,1808 <Error<T>>::CollectionLimitBoundsExceeded,1809 ),1810 sponsored_data_size => ensure!(1811 new_limit <= CUSTOM_DATA_LIMIT,1812 <Error<T>>::CollectionLimitBoundsExceeded,1813 ),18141815 sponsored_data_rate_limit => {},1816 token_limit => ensure!(1817 old_limit >= new_limit && new_limit > 0,1818 <Error<T>>::CollectionTokenLimitExceeded1819 ),18201821 sponsor_transfer_timeout(match mode {1822 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1823 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1824 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1825 }) => ensure!(1826 new_limit <= MAX_SPONSOR_TIMEOUT,1827 <Error<T>>::CollectionLimitBoundsExceeded,1828 ),1829 sponsor_approve_timeout => {},1830 owner_can_transfer => ensure!(1831 !limits.owner_can_transfer_instaled() ||1832 old_limit || !new_limit,1833 <Error<T>>::OwnerPermissionsCantBeReverted,1834 ),1835 owner_can_destroy => ensure!(1836 old_limit || !new_limit,1837 <Error<T>>::OwnerPermissionsCantBeReverted,1838 ),1839 transfers_enabled => {},1840 );1841 Ok(new_limit)1842 }18431844 1845 pub fn update_permissions(1846 user: &T::CrossAccountId,1847 collection: &mut CollectionHandle<T>,1848 new_permission: CollectionPermissions,1849 ) -> DispatchResult {1850 collection.check_is_internal()?;1851 collection.check_is_owner_or_admin(user)?;1852 collection.permissions = Self::clamp_permissions(1853 collection.mode.clone(),1854 &collection.permissions,1855 new_permission,1856 )?;18571858 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1859 <PalletEvm<T>>::deposit_log(1860 erc::CollectionHelpersEvents::CollectionChanged {1861 collection_id: eth::collection_id_to_address(collection.id),1862 }1863 .to_log(T::ContractAddress::get()),1864 );18651866 collection.save()1867 }18681869 1870 fn clamp_permissions(1871 _mode: CollectionMode,1872 old_permission: &CollectionPermissions,1873 mut new_permission: CollectionPermissions,1874 ) -> Result<CollectionPermissions, DispatchError> {1875 limit_default_clone!(old_permission, new_permission,1876 access => {},1877 mint_mode => {},1878 nesting => { },1879 );1880 Ok(new_permission)1881 }18821883 1884 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1885 CollectionProperties::<T>::mutate(collection_id, |properties| {1886 properties.recompute_consumed_space();1887 });18881889 Ok(())1890 }1891}189218931894#[macro_export]1895macro_rules! unsupported {1896 ($runtime:path) => {1897 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1898 };1899}190019011902pub trait CommonWeightInfo<CrossAccountId> {1903 1904 fn create_item(data: &CreateItemData) -> Weight {1905 Self::create_multiple_items(from_ref(data))1906 }19071908 1909 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;19101911 1912 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;19131914 1915 fn burn_item() -> Weight;19161917 1918 1919 1920 fn set_collection_properties(amount: u32) -> Weight;19211922 1923 1924 1925 fn delete_collection_properties(amount: u32) -> Weight;19261927 1928 1929 1930 fn set_token_properties(amount: u32) -> Weight;19311932 1933 1934 1935 fn delete_token_properties(amount: u32) -> Weight;19361937 1938 1939 1940 fn set_token_property_permissions(amount: u32) -> Weight;19411942 1943 fn transfer() -> Weight;19441945 1946 fn approve() -> Weight;19471948 1949 fn approve_from() -> Weight;19501951 1952 fn transfer_from() -> Weight;19531954 1955 fn burn_from() -> Weight;19561957 1958 1959 1960 1961 fn burn_recursively_self_raw() -> Weight;19621963 1964 1965 1966 fn burn_recursively_breadth_raw(amount: u32) -> Weight;19671968 1969 1970 1971 1972 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1973 Self::burn_recursively_self_raw()1974 .saturating_mul(max_selfs.max(1) as u64)1975 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1976 }19771978 1979 fn token_owner() -> Weight;19801981 1982 fn set_allowance_for_all() -> Weight;19831984 1985 fn force_repair_item() -> Weight;1986}198719881989pub trait RefungibleExtensionsWeightInfo {1990 1991 fn repartition() -> Weight;1992}199319941995199619971998pub trait CommonCollectionOperations<T: Config> {1999 2000 2001 2002 2003 2004 2005 fn create_item(2006 &self,2007 sender: T::CrossAccountId,2008 to: T::CrossAccountId,2009 data: CreateItemData,2010 nesting_budget: &dyn Budget,2011 ) -> DispatchResultWithPostInfo;20122013 2014 2015 2016 2017 2018 2019 fn create_multiple_items(2020 &self,2021 sender: T::CrossAccountId,2022 to: T::CrossAccountId,2023 data: Vec<CreateItemData>,2024 nesting_budget: &dyn Budget,2025 ) -> DispatchResultWithPostInfo;20262027 2028 2029 2030 2031 2032 2033 fn create_multiple_items_ex(2034 &self,2035 sender: T::CrossAccountId,2036 data: CreateItemExData<T::CrossAccountId>,2037 nesting_budget: &dyn Budget,2038 ) -> DispatchResultWithPostInfo;20392040 2041 2042 2043 2044 2045 fn burn_item(2046 &self,2047 sender: T::CrossAccountId,2048 token: TokenId,2049 amount: u128,2050 ) -> DispatchResultWithPostInfo;20512052 2053 2054 2055 2056 2057 2058 fn burn_item_recursively(2059 &self,2060 sender: T::CrossAccountId,2061 token: TokenId,2062 self_budget: &dyn Budget,2063 breadth_budget: &dyn Budget,2064 ) -> DispatchResultWithPostInfo;20652066 2067 2068 2069 2070 fn set_collection_properties(2071 &self,2072 sender: T::CrossAccountId,2073 properties: Vec<Property>,2074 ) -> DispatchResultWithPostInfo;20752076 2077 2078 2079 2080 fn delete_collection_properties(2081 &self,2082 sender: &T::CrossAccountId,2083 property_keys: Vec<PropertyKey>,2084 ) -> DispatchResultWithPostInfo;20852086 2087 2088 2089 2090 2091 2092 2093 2094 2095 fn set_token_properties(2096 &self,2097 sender: T::CrossAccountId,2098 token_id: TokenId,2099 properties: Vec<Property>,2100 budget: &dyn Budget,2101 ) -> DispatchResultWithPostInfo;21022103 2104 2105 2106 2107 2108 2109 2110 2111 2112 fn delete_token_properties(2113 &self,2114 sender: T::CrossAccountId,2115 token_id: TokenId,2116 property_keys: Vec<PropertyKey>,2117 budget: &dyn Budget,2118 ) -> DispatchResultWithPostInfo;21192120 2121 2122 2123 2124 2125 2126 fn set_token_property_permissions(2127 &self,2128 sender: &T::CrossAccountId,2129 property_permissions: Vec<PropertyKeyPermission>,2130 ) -> DispatchResultWithPostInfo;21312132 2133 2134 2135 2136 2137 2138 2139 fn transfer(2140 &self,2141 sender: T::CrossAccountId,2142 to: T::CrossAccountId,2143 token: TokenId,2144 amount: u128,2145 budget: &dyn Budget,2146 ) -> DispatchResultWithPostInfo;21472148 2149 2150 2151 2152 2153 2154 fn approve(2155 &self,2156 sender: T::CrossAccountId,2157 spender: T::CrossAccountId,2158 token: TokenId,2159 amount: u128,2160 ) -> DispatchResultWithPostInfo;21612162 2163 2164 2165 2166 2167 2168 2169 fn approve_from(2170 &self,2171 sender: T::CrossAccountId,2172 from: T::CrossAccountId,2173 to: T::CrossAccountId,2174 token: TokenId,2175 amount: u128,2176 ) -> DispatchResultWithPostInfo;21772178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 fn transfer_from(2189 &self,2190 sender: T::CrossAccountId,2191 from: T::CrossAccountId,2192 to: T::CrossAccountId,2193 token: TokenId,2194 amount: u128,2195 budget: &dyn Budget,2196 ) -> DispatchResultWithPostInfo;21972198 2199 2200 2201 2202 2203 2204 2205 2206 2207 fn burn_from(2208 &self,2209 sender: T::CrossAccountId,2210 from: T::CrossAccountId,2211 token: TokenId,2212 amount: u128,2213 budget: &dyn Budget,2214 ) -> DispatchResultWithPostInfo;22152216 2217 2218 2219 2220 2221 2222 fn check_nesting(2223 &self,2224 sender: T::CrossAccountId,2225 from: (CollectionId, TokenId),2226 under: TokenId,2227 budget: &dyn Budget,2228 ) -> DispatchResult;22292230 2231 2232 2233 2234 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22352236 2237 2238 2239 2240 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22412242 2243 2244 2245 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;22462247 2248 fn collection_tokens(&self) -> Vec<TokenId>;22492250 2251 2252 2253 fn token_exists(&self, token: TokenId) -> bool;22542255 2256 fn last_token_id(&self) -> TokenId;22572258 2259 2260 2261 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22622263 2264 2265 2266 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22672268 2269 2270 2271 2272 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22732274 2275 2276 2277 2278 2279 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22802281 2282 fn total_supply(&self) -> u32;22832284 2285 2286 2287 fn account_balance(&self, account: T::CrossAccountId) -> u32;22882289 2290 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;22912292 2293 fn total_pieces(&self, token: TokenId) -> Option<u128>;22942295 2296 2297 2298 2299 2300 fn allowance(2301 &self,2302 sender: T::CrossAccountId,2303 spender: T::CrossAccountId,2304 token: TokenId,2305 ) -> u128;23062307 2308 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;23092310 2311 2312 2313 2314 fn set_allowance_for_all(2315 &self,2316 owner: T::CrossAccountId,2317 operator: T::CrossAccountId,2318 approve: bool,2319 ) -> DispatchResultWithPostInfo;23202321 2322 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;23232324 2325 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2326}232723282329pub trait RefungibleExtensions<T>2330where2331 T: Config,2332{2333 2334 2335 2336 2337 2338 2339 2340 fn repartition(2341 &self,2342 sender: &T::CrossAccountId,2343 token: TokenId,2344 amount: u128,2345 ) -> DispatchResultWithPostInfo;2346}23472348234923502351pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2352 let post_info = PostDispatchInfo {2353 actual_weight: Some(weight),2354 pays_fee: Pays::Yes,2355 };2356 match res {2357 Ok(()) => Ok(post_info),2358 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2359 }2360}23612362impl<T: Config> From<PropertiesError> for Error<T> {2363 fn from(error: PropertiesError) -> Self {2364 match error {2365 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2366 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2367 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2368 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2369 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2370 }2371 }2372}