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,70};71use pallet_evm::GasWeightMapping;72use up_data_structs::{73 COLLECTION_NUMBER_LIMIT,74 Collection,75 RpcCollection,76 CollectionFlags,77 RpcCollectionFlags,78 CollectionId,79 CreateItemData,80 MAX_TOKEN_PREFIX_LENGTH,81 COLLECTION_ADMINS_LIMIT,82 TokenId,83 TokenChild,84 CollectionStats,85 MAX_TOKEN_OWNERSHIP,86 CollectionMode,87 NFT_SPONSOR_TRANSFER_TIMEOUT,88 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,89 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,90 MAX_SPONSOR_TIMEOUT,91 CUSTOM_DATA_LIMIT,92 CollectionLimits,93 CreateCollectionData,94 SponsorshipState,95 CreateItemExData,96 SponsoringRateLimit,97 budget::Budget,98 PhantomType,99 Property,100 Properties,101 PropertiesPermissionMap,102 PropertyKey,103 PropertyValue,104 PropertyPermission,105 PropertiesError,106 TokenOwnerError,107 PropertyKeyPermission,108 TokenData,109 TrySetProperty,110 PropertyScope,111 112 RmrkCollectionInfo,113 RmrkInstanceInfo,114 RmrkResourceInfo,115 RmrkPropertyInfo,116 RmrkBaseInfo,117 RmrkPartType,118 RmrkBoundedTheme,119 RmrkNftChild,120 CollectionPermissions,121};122use up_pov_estimate_rpc::PovInfo;123124pub use pallet::*;125use sp_core::H160;126use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};127#[cfg(feature = "runtime-benchmarks")]128pub mod benchmarking;129pub mod dispatch;130pub mod erc;131pub mod eth;132pub mod weights;133134135pub type SelfWeightOf<T> = <T as Config>::WeightInfo;136137138139140141142143#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]144pub struct CollectionHandle<T: Config> {145 146 pub id: CollectionId,147 collection: Collection<T::AccountId>,148 149 pub recorder: SubstrateRecorder<T>,150}151152impl<T: Config> WithRecorder<T> for CollectionHandle<T> {153 fn recorder(&self) -> &SubstrateRecorder<T> {154 &self.recorder155 }156 fn into_recorder(self) -> SubstrateRecorder<T> {157 self.recorder158 }159}160161impl<T: Config> CollectionHandle<T> {162 163 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {164 <CollectionById<T>>::get(id).map(|collection| Self {165 id,166 collection,167 recorder: SubstrateRecorder::new(gas_limit),168 })169 }170171 172 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {173 <CollectionById<T>>::get(id).map(|collection| Self {174 id,175 collection,176 recorder,177 })178 }179180 181 182 pub fn new(id: CollectionId) -> Option<Self> {183 Self::new_with_gas_limit(id, u64::MAX)184 }185186 187 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {188 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)189 }190191 192 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {193 self.recorder194 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(195 <T as frame_system::Config>::DbWeight::get()196 .read197 .saturating_mul(reads),198 )))199 }200201 202 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::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 ) -> evm_coder::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 RmrkCollectionInfo<T::AccountId>,894 RmrkInstanceInfo<T::AccountId>,895 RmrkResourceInfo,896 RmrkPropertyInfo,897 RmrkBaseInfo<T::AccountId>,898 RmrkPartType,899 RmrkBoundedTheme,900 RmrkNftChild,901 902 PovInfo,903 )>,904 ),905 QueryKind = OptionQuery,906 >;907908 #[pallet::hooks]909 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {910 fn on_runtime_upgrade() -> Weight {911 StorageVersion::new(1).put::<Pallet<T>>();912913 Weight::zero()914 }915 }916}917918impl<T: Config> Pallet<T> {919 920 921 922 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {923 ensure!(924 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,925 <Error<T>>::AddressIsZero926 );927 Ok(())928 }929930 931 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {932 <IsAdmin<T>>::iter_prefix((collection,))933 .map(|(a, _)| a)934 .collect()935 }936937 938 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {939 <Allowlist<T>>::iter_prefix((collection,))940 .map(|(a, _)| a)941 .collect()942 }943944 945 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {946 <Allowlist<T>>::get((collection, user))947 }948949 950 pub fn collection_stats() -> CollectionStats {951 let created = <CreatedCollectionCount<T>>::get();952 let destroyed = <DestroyedCollectionCount<T>>::get();953 CollectionStats {954 created: created.0,955 destroyed: destroyed.0,956 alive: created.0 - destroyed.0,957 }958 }959960 961 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {962 let collection = <CollectionById<T>>::get(collection)?;963 let limits = collection.limits;964 let effective_limits = CollectionLimits {965 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),966 sponsored_data_size: Some(limits.sponsored_data_size()),967 sponsored_data_rate_limit: Some(968 limits969 .sponsored_data_rate_limit970 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),971 ),972 token_limit: Some(limits.token_limit()),973 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(974 match collection.mode {975 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,976 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,977 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,978 },979 )),980 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),981 owner_can_transfer: Some(limits.owner_can_transfer()),982 owner_can_destroy: Some(limits.owner_can_destroy()),983 transfers_enabled: Some(limits.transfers_enabled()),984 };985986 Some(effective_limits)987 }988989 990 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {991 let Collection {992 name,993 description,994 owner,995 mode,996 token_prefix,997 sponsorship,998 limits,999 permissions,1000 flags,1001 } = <CollectionById<T>>::get(collection)?;10021003 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)1004 .into_iter()1005 .map(|(key, permission)| PropertyKeyPermission { key, permission })1006 .collect();10071008 let properties = <CollectionProperties<T>>::get(collection)1009 .into_iter()1010 .map(|(key, value)| Property { key, value })1011 .collect();10121013 let permissions = CollectionPermissions {1014 access: Some(permissions.access()),1015 mint_mode: Some(permissions.mint_mode()),1016 nesting: Some(permissions.nesting().clone()),1017 };10181019 Some(RpcCollection {1020 name: name.into_inner(),1021 description: description.into_inner(),1022 owner,1023 mode,1024 token_prefix: token_prefix.into_inner(),1025 sponsorship,1026 limits,1027 permissions,1028 token_property_permissions,1029 properties,1030 read_only: flags.external,10311032 flags: RpcCollectionFlags {1033 foreign: flags.foreign,1034 erc721metadata: flags.erc721metadata,1035 },1036 })1037 }1038}10391040macro_rules! limit_default {1041 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1042 $(1043 if let Some($new) = $new.$field {1044 let $old = $old.$field($($arg)?);1045 let _ = $new;1046 let _ = $old;1047 $check1048 } else {1049 $new.$field = $old.$field1050 }1051 )*1052 }};1053}1054macro_rules! limit_default_clone {1055 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1056 $(1057 if let Some($new) = $new.$field.clone() {1058 let $old = $old.$field($($arg)?);1059 let _ = $new;1060 let _ = $old;1061 $check1062 } else {1063 $new.$field = $old.$field.clone()1064 }1065 )*1066 }};1067}10681069impl<T: Config> Pallet<T> {1070 1071 1072 1073 1074 1075 pub fn init_collection(1076 owner: T::CrossAccountId,1077 payer: T::CrossAccountId,1078 data: CreateCollectionData<T::AccountId>,1079 flags: CollectionFlags,1080 ) -> Result<CollectionId, DispatchError> {1081 {1082 ensure!(1083 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1084 Error::<T>::CollectionTokenPrefixLimitExceeded1085 );1086 }10871088 let created_count = <CreatedCollectionCount<T>>::get()1089 .01090 .checked_add(1)1091 .ok_or(ArithmeticError::Overflow)?;1092 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1093 let id = CollectionId(created_count);10941095 1096 ensure!(1097 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1098 <Error<T>>::TotalCollectionsLimitExceeded1099 );11001101 11021103 let collection = Collection {1104 owner: owner.as_sub().clone(),1105 name: data.name,1106 mode: data.mode.clone(),1107 description: data.description,1108 token_prefix: data.token_prefix,1109 sponsorship: data1110 .pending_sponsor1111 .map(SponsorshipState::Unconfirmed)1112 .unwrap_or_default(),1113 limits: data1114 .limits1115 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1116 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1117 permissions: data1118 .permissions1119 .map(|permissions| {1120 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1121 })1122 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1123 flags,1124 };11251126 let mut collection_properties = up_data_structs::CollectionProperties::get();1127 collection_properties1128 .try_set_from_iter(data.properties.into_iter())1129 .map_err(<Error<T>>::from)?;11301131 CollectionProperties::<T>::insert(id, collection_properties);11321133 let mut token_props_permissions = PropertiesPermissionMap::new();1134 token_props_permissions1135 .try_set_from_iter(data.token_property_permissions.into_iter())1136 .map_err(<Error<T>>::from)?;11371138 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11391140 1141 {1142 let mut imbalance =1143 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1144 imbalance.subsume(1145 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1146 &T::TreasuryAccountId::get(),1147 T::CollectionCreationPrice::get(),1148 ),1149 );1150 <T as Config>::Currency::settle(1151 payer.as_sub(),1152 imbalance,1153 WithdrawReasons::TRANSFER,1154 ExistenceRequirement::KeepAlive,1155 )1156 .map_err(|_| Error::<T>::NotSufficientFounds)?;1157 }11581159 <CreatedCollectionCount<T>>::put(created_count);1160 <Pallet<T>>::deposit_event(Event::CollectionCreated(1161 id,1162 data.mode.id(),1163 owner.as_sub().clone(),1164 ));1165 <PalletEvm<T>>::deposit_log(1166 erc::CollectionHelpersEvents::CollectionCreated {1167 owner: *owner.as_eth(),1168 collection_id: eth::collection_id_to_address(id),1169 }1170 .to_log(T::ContractAddress::get()),1171 );1172 <CollectionById<T>>::insert(id, collection);1173 Ok(id)1174 }11751176 1177 1178 1179 1180 pub fn destroy_collection(1181 collection: CollectionHandle<T>,1182 sender: &T::CrossAccountId,1183 ) -> DispatchResult {1184 ensure!(1185 collection.limits.owner_can_destroy(),1186 <Error<T>>::NoPermission,1187 );1188 collection.check_is_owner(sender)?;11891190 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1191 .01192 .checked_add(1)1193 .ok_or(ArithmeticError::Overflow)?;11941195 11961197 <DestroyedCollectionCount<T>>::put(destroyed_collections);1198 <CollectionById<T>>::remove(collection.id);1199 <AdminAmount<T>>::remove(collection.id);1200 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1201 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1202 <CollectionProperties<T>>::remove(collection.id);12031204 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));12051206 <PalletEvm<T>>::deposit_log(1207 erc::CollectionHelpersEvents::CollectionDestroyed {1208 collection_id: eth::collection_id_to_address(collection.id),1209 }1210 .to_log(T::ContractAddress::get()),1211 );1212 Ok(())1213 }12141215 1216 1217 1218 1219 1220 1221 1222 1223 #[transactional]1224 fn modify_collection_properties(1225 collection: &CollectionHandle<T>,1226 sender: &T::CrossAccountId,1227 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1228 ) -> DispatchResult {1229 collection.check_is_owner_or_admin(sender)?;12301231 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12321233 for (key, value) in properties_updates {1234 match value {1235 Some(value) => {1236 stored_properties1237 .try_set(key.clone(), value)1238 .map_err(<Error<T>>::from)?;12391240 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1241 <PalletEvm<T>>::deposit_log(1242 erc::CollectionHelpersEvents::CollectionChanged {1243 collection_id: eth::collection_id_to_address(collection.id),1244 }1245 .to_log(T::ContractAddress::get()),1246 );1247 }1248 None => {1249 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12501251 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1252 <PalletEvm<T>>::deposit_log(1253 erc::CollectionHelpersEvents::CollectionChanged {1254 collection_id: eth::collection_id_to_address(collection.id),1255 }1256 .to_log(T::ContractAddress::get()),1257 );1258 }1259 }1260 }12611262 <CollectionProperties<T>>::set(collection.id, stored_properties);12631264 Ok(())1265 }12661267 1268 1269 1270 1271 1272 pub fn set_collection_property(1273 collection: &CollectionHandle<T>,1274 sender: &T::CrossAccountId,1275 property: Property,1276 ) -> DispatchResult {1277 Self::set_collection_properties(collection, sender, [property].into_iter())1278 }12791280 1281 1282 1283 1284 1285 1286 pub fn set_scoped_collection_property(1287 collection_id: CollectionId,1288 scope: PropertyScope,1289 property: Property,1290 ) -> DispatchResult {1291 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1292 properties.try_scoped_set(scope, property.key, property.value)1293 })1294 .map_err(<Error<T>>::from)?;12951296 Ok(())1297 }12981299 1300 1301 1302 1303 1304 1305 pub fn set_scoped_collection_properties(1306 collection_id: CollectionId,1307 scope: PropertyScope,1308 properties: impl Iterator<Item = Property>,1309 ) -> DispatchResult {1310 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1311 stored_properties.try_scoped_set_from_iter(scope, properties)1312 })1313 .map_err(<Error<T>>::from)?;13141315 Ok(())1316 }13171318 1319 1320 1321 1322 1323 pub fn set_collection_properties(1324 collection: &CollectionHandle<T>,1325 sender: &T::CrossAccountId,1326 properties: impl Iterator<Item = Property>,1327 ) -> DispatchResult {1328 Self::modify_collection_properties(1329 collection,1330 sender,1331 properties.map(|property| (property.key, Some(property.value))),1332 )1333 }13341335 1336 1337 1338 1339 1340 pub fn delete_collection_property(1341 collection: &CollectionHandle<T>,1342 sender: &T::CrossAccountId,1343 property_key: PropertyKey,1344 ) -> DispatchResult {1345 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1346 }13471348 1349 1350 1351 1352 1353 pub fn delete_collection_properties(1354 collection: &CollectionHandle<T>,1355 sender: &T::CrossAccountId,1356 property_keys: impl Iterator<Item = PropertyKey>,1357 ) -> DispatchResult {1358 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1359 }13601361 1362 1363 1364 1365 1366 1367 pub fn set_property_permission_unchecked(1368 collection: CollectionId,1369 property_permission: PropertyKeyPermission,1370 ) -> DispatchResult {1371 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1372 permissions.try_set(property_permission.key, property_permission.permission)1373 })1374 .map_err(<Error<T>>::from)?;1375 Ok(())1376 }13771378 1379 1380 1381 1382 1383 pub fn set_property_permission(1384 collection: &CollectionHandle<T>,1385 sender: &T::CrossAccountId,1386 property_permission: PropertyKeyPermission,1387 ) -> DispatchResult {1388 Self::set_scoped_property_permission(1389 collection,1390 sender,1391 PropertyScope::None,1392 property_permission,1393 )1394 }13951396 1397 1398 1399 1400 1401 1402 pub fn set_scoped_property_permission(1403 collection: &CollectionHandle<T>,1404 sender: &T::CrossAccountId,1405 scope: PropertyScope,1406 property_permission: PropertyKeyPermission,1407 ) -> DispatchResult {1408 collection.check_is_owner_or_admin(sender)?;14091410 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1411 let current_permission = all_permissions.get(&property_permission.key);1412 if matches![1413 current_permission,1414 Some(PropertyPermission { mutable: false, .. })1415 ] {1416 return Err(<Error<T>>::NoPermission.into());1417 }14181419 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1420 let property_permission = property_permission.clone();1421 permissions.try_scoped_set(1422 scope,1423 property_permission.key,1424 property_permission.permission,1425 )1426 })1427 .map_err(<Error<T>>::from)?;14281429 Self::deposit_event(Event::PropertyPermissionSet(1430 collection.id,1431 property_permission.key,1432 ));1433 <PalletEvm<T>>::deposit_log(1434 erc::CollectionHelpersEvents::CollectionChanged {1435 collection_id: eth::collection_id_to_address(collection.id),1436 }1437 .to_log(T::ContractAddress::get()),1438 );14391440 Ok(())1441 }14421443 1444 1445 1446 1447 1448 #[transactional]1449 pub fn set_token_property_permissions(1450 collection: &CollectionHandle<T>,1451 sender: &T::CrossAccountId,1452 property_permissions: Vec<PropertyKeyPermission>,1453 ) -> DispatchResult {1454 Self::set_scoped_token_property_permissions(1455 collection,1456 sender,1457 PropertyScope::None,1458 property_permissions,1459 )1460 }14611462 1463 1464 1465 1466 1467 1468 #[transactional]1469 pub fn set_scoped_token_property_permissions(1470 collection: &CollectionHandle<T>,1471 sender: &T::CrossAccountId,1472 scope: PropertyScope,1473 property_permissions: Vec<PropertyKeyPermission>,1474 ) -> DispatchResult {1475 for prop_pemission in property_permissions {1476 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1477 }14781479 Ok(())1480 }14811482 1483 pub fn get_collection_property(1484 collection_id: CollectionId,1485 key: &PropertyKey,1486 ) -> Option<PropertyValue> {1487 Self::collection_properties(collection_id).get(key).cloned()1488 }14891490 1491 pub fn bytes_keys_to_property_keys(1492 keys: Vec<Vec<u8>>,1493 ) -> Result<Vec<PropertyKey>, DispatchError> {1494 keys.into_iter()1495 .map(|key| -> Result<PropertyKey, DispatchError> {1496 key.try_into()1497 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1498 })1499 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1500 }15011502 1503 pub fn filter_collection_properties(1504 collection_id: CollectionId,1505 keys: Option<Vec<PropertyKey>>,1506 ) -> Result<Vec<Property>, DispatchError> {1507 let properties = Self::collection_properties(collection_id);15081509 let properties = keys1510 .map(|keys| {1511 keys.into_iter()1512 .filter_map(|key| {1513 properties.get(&key).map(|value| Property {1514 key,1515 value: value.clone(),1516 })1517 })1518 .collect()1519 })1520 .unwrap_or_else(|| {1521 properties1522 .into_iter()1523 .map(|(key, value)| Property { key, value })1524 .collect()1525 });15261527 Ok(properties)1528 }15291530 1531 pub fn filter_property_permissions(1532 collection_id: CollectionId,1533 keys: Option<Vec<PropertyKey>>,1534 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1535 let permissions = Self::property_permissions(collection_id);15361537 let key_permissions = keys1538 .map(|keys| {1539 keys.into_iter()1540 .filter_map(|key| {1541 permissions1542 .get(&key)1543 .map(|permission| PropertyKeyPermission {1544 key,1545 permission: permission.clone(),1546 })1547 })1548 .collect()1549 })1550 .unwrap_or_else(|| {1551 permissions1552 .into_iter()1553 .map(|(key, permission)| PropertyKeyPermission { key, permission })1554 .collect()1555 });15561557 Ok(key_permissions)1558 }15591560 1561 1562 1563 pub fn toggle_allowlist(1564 collection: &CollectionHandle<T>,1565 sender: &T::CrossAccountId,1566 user: &T::CrossAccountId,1567 allowed: bool,1568 ) -> DispatchResult {1569 collection.check_is_owner_or_admin(sender)?;15701571 15721573 if allowed {1574 <Allowlist<T>>::insert((collection.id, user), true);1575 Self::deposit_event(Event::<T>::AllowListAddressAdded(1576 collection.id,1577 user.clone(),1578 ));1579 } else {1580 <Allowlist<T>>::remove((collection.id, user));1581 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1582 collection.id,1583 user.clone(),1584 ));1585 }15861587 <PalletEvm<T>>::deposit_log(1588 erc::CollectionHelpersEvents::CollectionChanged {1589 collection_id: eth::collection_id_to_address(collection.id),1590 }1591 .to_log(T::ContractAddress::get()),1592 );15931594 Ok(())1595 }15961597 1598 1599 1600 pub fn toggle_admin(1601 collection: &CollectionHandle<T>,1602 sender: &T::CrossAccountId,1603 user: &T::CrossAccountId,1604 admin: bool,1605 ) -> DispatchResult {1606 collection.check_is_internal()?;1607 collection.check_is_owner(sender)?;16081609 let is_admin = <IsAdmin<T>>::get((collection.id, user));1610 if is_admin == admin {1611 if admin {1612 return Ok(());1613 } else {1614 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1615 }1616 }1617 let amount = <AdminAmount<T>>::get(collection.id);16181619 16201621 if admin {1622 let amount = amount1623 .checked_add(1)1624 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1625 ensure!(1626 amount <= Self::collection_admins_limit(),1627 <Error<T>>::CollectionAdminCountExceeded,1628 );16291630 <AdminAmount<T>>::insert(collection.id, amount);1631 <IsAdmin<T>>::insert((collection.id, user), true);16321633 Self::deposit_event(Event::<T>::CollectionAdminAdded(1634 collection.id,1635 user.clone(),1636 ));1637 } else {1638 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1639 <IsAdmin<T>>::remove((collection.id, user));16401641 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1642 collection.id,1643 user.clone(),1644 ));1645 }16461647 <PalletEvm<T>>::deposit_log(1648 erc::CollectionHelpersEvents::CollectionChanged {1649 collection_id: eth::collection_id_to_address(collection.id),1650 }1651 .to_log(T::ContractAddress::get()),1652 );16531654 Ok(())1655 }16561657 1658 pub fn update_limits(1659 user: &T::CrossAccountId,1660 collection: &mut CollectionHandle<T>,1661 new_limit: CollectionLimits,1662 ) -> DispatchResult {1663 collection.check_is_internal()?;1664 collection.check_is_owner_or_admin(user)?;16651666 collection.limits =1667 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16681669 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1670 <PalletEvm<T>>::deposit_log(1671 erc::CollectionHelpersEvents::CollectionChanged {1672 collection_id: eth::collection_id_to_address(collection.id),1673 }1674 .to_log(T::ContractAddress::get()),1675 );16761677 collection.save()1678 }16791680 1681 fn clamp_limits(1682 mode: CollectionMode,1683 old_limit: &CollectionLimits,1684 mut new_limit: CollectionLimits,1685 ) -> Result<CollectionLimits, DispatchError> {1686 let limits = old_limit;1687 limit_default!(old_limit, new_limit,1688 account_token_ownership_limit => ensure!(1689 new_limit <= MAX_TOKEN_OWNERSHIP,1690 <Error<T>>::CollectionLimitBoundsExceeded,1691 ),1692 sponsored_data_size => ensure!(1693 new_limit <= CUSTOM_DATA_LIMIT,1694 <Error<T>>::CollectionLimitBoundsExceeded,1695 ),16961697 sponsored_data_rate_limit => {},1698 token_limit => ensure!(1699 old_limit >= new_limit && new_limit > 0,1700 <Error<T>>::CollectionTokenLimitExceeded1701 ),17021703 sponsor_transfer_timeout(match mode {1704 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1705 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1706 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1707 }) => ensure!(1708 new_limit <= MAX_SPONSOR_TIMEOUT,1709 <Error<T>>::CollectionLimitBoundsExceeded,1710 ),1711 sponsor_approve_timeout => {},1712 owner_can_transfer => ensure!(1713 !limits.owner_can_transfer_instaled() ||1714 old_limit || !new_limit,1715 <Error<T>>::OwnerPermissionsCantBeReverted,1716 ),1717 owner_can_destroy => ensure!(1718 old_limit || !new_limit,1719 <Error<T>>::OwnerPermissionsCantBeReverted,1720 ),1721 transfers_enabled => {},1722 );1723 Ok(new_limit)1724 }17251726 1727 pub fn update_permissions(1728 user: &T::CrossAccountId,1729 collection: &mut CollectionHandle<T>,1730 new_permission: CollectionPermissions,1731 ) -> DispatchResult {1732 collection.check_is_internal()?;1733 collection.check_is_owner_or_admin(user)?;1734 collection.permissions = Self::clamp_permissions(1735 collection.mode.clone(),1736 &collection.permissions,1737 new_permission,1738 )?;17391740 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1741 <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 collection.save()1749 }17501751 1752 fn clamp_permissions(1753 _mode: CollectionMode,1754 old_permission: &CollectionPermissions,1755 mut new_permission: CollectionPermissions,1756 ) -> Result<CollectionPermissions, DispatchError> {1757 limit_default_clone!(old_permission, new_permission,1758 access => {},1759 mint_mode => {},1760 nesting => { },1761 );1762 Ok(new_permission)1763 }17641765 1766 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1767 CollectionProperties::<T>::mutate(collection_id, |properties| {1768 properties.recompute_consumed_space();1769 });17701771 Ok(())1772 }1773}177417751776#[macro_export]1777macro_rules! unsupported {1778 ($runtime:path) => {1779 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1780 };1781}178217831784pub trait CommonWeightInfo<CrossAccountId> {1785 1786 fn create_item(data: &CreateItemData) -> Weight {1787 Self::create_multiple_items(from_ref(data))1788 }17891790 1791 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17921793 1794 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17951796 1797 fn burn_item() -> Weight;17981799 1800 1801 1802 fn set_collection_properties(amount: u32) -> Weight;18031804 1805 1806 1807 fn delete_collection_properties(amount: u32) -> Weight;18081809 1810 1811 1812 fn set_token_properties(amount: u32) -> Weight;18131814 1815 1816 1817 fn delete_token_properties(amount: u32) -> Weight;18181819 1820 1821 1822 fn set_token_property_permissions(amount: u32) -> Weight;18231824 1825 fn transfer() -> Weight;18261827 1828 fn approve() -> Weight;18291830 1831 fn approve_from() -> Weight;18321833 1834 fn transfer_from() -> Weight;18351836 1837 fn burn_from() -> Weight;18381839 1840 1841 1842 1843 fn burn_recursively_self_raw() -> Weight;18441845 1846 1847 1848 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18491850 1851 1852 1853 1854 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1855 Self::burn_recursively_self_raw()1856 .saturating_mul(max_selfs.max(1) as u64)1857 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1858 }18591860 1861 fn token_owner() -> Weight;18621863 1864 fn set_allowance_for_all() -> Weight;18651866 1867 fn force_repair_item() -> Weight;1868}186918701871pub trait RefungibleExtensionsWeightInfo {1872 1873 fn repartition() -> Weight;1874}187518761877187818791880pub trait CommonCollectionOperations<T: Config> {1881 1882 1883 1884 1885 1886 1887 fn create_item(1888 &self,1889 sender: T::CrossAccountId,1890 to: T::CrossAccountId,1891 data: CreateItemData,1892 nesting_budget: &dyn Budget,1893 ) -> DispatchResultWithPostInfo;18941895 1896 1897 1898 1899 1900 1901 fn create_multiple_items(1902 &self,1903 sender: T::CrossAccountId,1904 to: T::CrossAccountId,1905 data: Vec<CreateItemData>,1906 nesting_budget: &dyn Budget,1907 ) -> DispatchResultWithPostInfo;19081909 1910 1911 1912 1913 1914 1915 fn create_multiple_items_ex(1916 &self,1917 sender: T::CrossAccountId,1918 data: CreateItemExData<T::CrossAccountId>,1919 nesting_budget: &dyn Budget,1920 ) -> DispatchResultWithPostInfo;19211922 1923 1924 1925 1926 1927 fn burn_item(1928 &self,1929 sender: T::CrossAccountId,1930 token: TokenId,1931 amount: u128,1932 ) -> DispatchResultWithPostInfo;19331934 1935 1936 1937 1938 1939 1940 fn burn_item_recursively(1941 &self,1942 sender: T::CrossAccountId,1943 token: TokenId,1944 self_budget: &dyn Budget,1945 breadth_budget: &dyn Budget,1946 ) -> DispatchResultWithPostInfo;19471948 1949 1950 1951 1952 fn set_collection_properties(1953 &self,1954 sender: T::CrossAccountId,1955 properties: Vec<Property>,1956 ) -> DispatchResultWithPostInfo;19571958 1959 1960 1961 1962 fn delete_collection_properties(1963 &self,1964 sender: &T::CrossAccountId,1965 property_keys: Vec<PropertyKey>,1966 ) -> DispatchResultWithPostInfo;19671968 1969 1970 1971 1972 1973 1974 1975 1976 1977 fn set_token_properties(1978 &self,1979 sender: T::CrossAccountId,1980 token_id: TokenId,1981 properties: Vec<Property>,1982 budget: &dyn Budget,1983 ) -> DispatchResultWithPostInfo;19841985 1986 1987 1988 1989 1990 1991 1992 1993 1994 fn delete_token_properties(1995 &self,1996 sender: T::CrossAccountId,1997 token_id: TokenId,1998 property_keys: Vec<PropertyKey>,1999 budget: &dyn Budget,2000 ) -> DispatchResultWithPostInfo;20012002 2003 2004 2005 2006 2007 2008 fn set_token_property_permissions(2009 &self,2010 sender: &T::CrossAccountId,2011 property_permissions: Vec<PropertyKeyPermission>,2012 ) -> DispatchResultWithPostInfo;20132014 2015 2016 2017 2018 2019 2020 2021 fn transfer(2022 &self,2023 sender: T::CrossAccountId,2024 to: T::CrossAccountId,2025 token: TokenId,2026 amount: u128,2027 budget: &dyn Budget,2028 ) -> DispatchResultWithPostInfo;20292030 2031 2032 2033 2034 2035 2036 fn approve(2037 &self,2038 sender: T::CrossAccountId,2039 spender: T::CrossAccountId,2040 token: TokenId,2041 amount: u128,2042 ) -> DispatchResultWithPostInfo;20432044 2045 2046 2047 2048 2049 2050 2051 fn approve_from(2052 &self,2053 sender: T::CrossAccountId,2054 from: T::CrossAccountId,2055 to: T::CrossAccountId,2056 token: TokenId,2057 amount: u128,2058 ) -> DispatchResultWithPostInfo;20592060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 fn transfer_from(2071 &self,2072 sender: T::CrossAccountId,2073 from: T::CrossAccountId,2074 to: T::CrossAccountId,2075 token: TokenId,2076 amount: u128,2077 budget: &dyn Budget,2078 ) -> DispatchResultWithPostInfo;20792080 2081 2082 2083 2084 2085 2086 2087 2088 2089 fn burn_from(2090 &self,2091 sender: T::CrossAccountId,2092 from: T::CrossAccountId,2093 token: TokenId,2094 amount: u128,2095 budget: &dyn Budget,2096 ) -> DispatchResultWithPostInfo;20972098 2099 2100 2101 2102 2103 2104 fn check_nesting(2105 &self,2106 sender: T::CrossAccountId,2107 from: (CollectionId, TokenId),2108 under: TokenId,2109 budget: &dyn Budget,2110 ) -> DispatchResult;21112112 2113 2114 2115 2116 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21172118 2119 2120 2121 2122 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21232124 2125 2126 2127 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21282129 2130 fn collection_tokens(&self) -> Vec<TokenId>;21312132 2133 2134 2135 fn token_exists(&self, token: TokenId) -> bool;21362137 2138 fn last_token_id(&self) -> TokenId;21392140 2141 2142 2143 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;21442145 2146 2147 2148 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;21492150 2151 2152 2153 2154 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21552156 2157 2158 2159 2160 2161 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21622163 2164 fn total_supply(&self) -> u32;21652166 2167 2168 2169 fn account_balance(&self, account: T::CrossAccountId) -> u32;21702171 2172 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21732174 2175 fn total_pieces(&self, token: TokenId) -> Option<u128>;21762177 2178 2179 2180 2181 2182 fn allowance(2183 &self,2184 sender: T::CrossAccountId,2185 spender: T::CrossAccountId,2186 token: TokenId,2187 ) -> u128;21882189 2190 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21912192 2193 2194 2195 2196 fn set_allowance_for_all(2197 &self,2198 owner: T::CrossAccountId,2199 operator: T::CrossAccountId,2200 approve: bool,2201 ) -> DispatchResultWithPostInfo;22022203 2204 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;22052206 2207 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2208}220922102211pub trait RefungibleExtensions<T>2212where2213 T: Config,2214{2215 2216 2217 2218 2219 2220 2221 2222 fn repartition(2223 &self,2224 sender: &T::CrossAccountId,2225 token: TokenId,2226 amount: u128,2227 ) -> DispatchResultWithPostInfo;2228}22292230223122322233pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2234 let post_info = PostDispatchInfo {2235 actual_weight: Some(weight),2236 pays_fee: Pays::Yes,2237 };2238 match res {2239 Ok(()) => Ok(post_info),2240 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2241 }2242}22432244impl<T: Config> From<PropertiesError> for Error<T> {2245 fn from(error: PropertiesError) -> Self {2246 match error {2247 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2248 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2249 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2250 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2251 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2252 }2253 }2254}