12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#![warn(missing_docs)]53#![cfg_attr(not(feature = "std"), no_std)]54extern crate alloc;5556use core::ops::{Deref, DerefMut};57use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};58use sp_std::vec::Vec;59use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};60use evm_coder::ToLog;61use frame_support::{62 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},63 ensure,64 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},65 dispatch::Pays,66 transactional,67};68use pallet_evm::GasWeightMapping;69use up_data_structs::{70 COLLECTION_NUMBER_LIMIT,71 Collection,72 RpcCollection,73 CollectionFlags,74 RpcCollectionFlags,75 CollectionId,76 CreateItemData,77 MAX_TOKEN_PREFIX_LENGTH,78 COLLECTION_ADMINS_LIMIT,79 TokenId,80 TokenChild,81 CollectionStats,82 MAX_TOKEN_OWNERSHIP,83 CollectionMode,84 NFT_SPONSOR_TRANSFER_TIMEOUT,85 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,86 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,87 MAX_SPONSOR_TIMEOUT,88 CUSTOM_DATA_LIMIT,89 CollectionLimits,90 CreateCollectionData,91 SponsorshipState,92 CreateItemExData,93 SponsoringRateLimit,94 budget::Budget,95 PhantomType,96 Property,97 Properties,98 PropertiesPermissionMap,99 PropertyKey,100 PropertyValue,101 PropertyPermission,102 PropertiesError,103 PropertyKeyPermission,104 TokenData,105 TrySetProperty,106 PropertyScope,107 108 RmrkCollectionInfo,109 RmrkInstanceInfo,110 RmrkResourceInfo,111 RmrkPropertyInfo,112 RmrkBaseInfo,113 RmrkPartType,114 RmrkBoundedTheme,115 RmrkNftChild,116 CollectionPermissions,117};118119pub use pallet::*;120use sp_core::H160;121use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};122#[cfg(feature = "runtime-benchmarks")]123pub mod benchmarking;124pub mod dispatch;125pub mod erc;126pub mod eth;127pub mod weights;128129130pub type SelfWeightOf<T> = <T as Config>::WeightInfo;131132133134135136137138#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]139pub struct CollectionHandle<T: Config> {140 141 pub id: CollectionId,142 collection: Collection<T::AccountId>,143 144 pub recorder: SubstrateRecorder<T>,145}146147impl<T: Config> WithRecorder<T> for CollectionHandle<T> {148 fn recorder(&self) -> &SubstrateRecorder<T> {149 &self.recorder150 }151 fn into_recorder(self) -> SubstrateRecorder<T> {152 self.recorder153 }154}155156impl<T: Config> CollectionHandle<T> {157 158 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {159 <CollectionById<T>>::get(id).map(|collection| Self {160 id,161 collection,162 recorder: SubstrateRecorder::new(gas_limit),163 })164 }165166 167 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {168 <CollectionById<T>>::get(id).map(|collection| Self {169 id,170 collection,171 recorder,172 })173 }174175 176 177 pub fn new(id: CollectionId) -> Option<Self> {178 Self::new_with_gas_limit(id, u64::MAX)179 }180181 182 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {183 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)184 }185186 187 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {188 self.recorder189 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(190 <T as frame_system::Config>::DbWeight::get()191 .read192 .saturating_mul(reads),193 )))194 }195196 197 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {198 self.recorder199 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(200 <T as frame_system::Config>::DbWeight::get()201 .write202 .saturating_mul(writes),203 )))204 }205206 207 pub fn consume_store_reads_and_writes(208 &self,209 reads: u64,210 writes: u64,211 ) -> evm_coder::execution::Result<()> {212 let weight = <T as frame_system::Config>::DbWeight::get();213 let reads = weight.read.saturating_mul(reads);214 let writes = weight.read.saturating_mul(writes);215 self.recorder216 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(217 reads.saturating_add(writes),218 )))219 }220221 222 pub fn save(&self) -> DispatchResult {223 <CollectionById<T>>::insert(self.id, &self.collection);224 Ok(())225 }226227 228 229 230 231 232 pub fn set_sponsor(233 &mut self,234 sender: &T::CrossAccountId,235 sponsor: T::AccountId,236 ) -> DispatchResult {237 self.check_is_internal()?;238 self.check_is_owner_or_admin(sender)?;239240 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());241242 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));243 <PalletEvm<T>>::deposit_log(244 erc::CollectionHelpersEvents::CollectionChanged {245 collection_id: eth::collection_id_to_address(self.id),246 }247 .to_log(T::ContractAddress::get()),248 );249250 self.save()251 }252253 254 255 256 257 258 259 260 261 262 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {263 self.check_is_internal()?;264265 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());266267 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));268 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));269 <PalletEvm<T>>::deposit_log(270 erc::CollectionHelpersEvents::CollectionChanged {271 collection_id: eth::collection_id_to_address(self.id),272 }273 .to_log(T::ContractAddress::get()),274 );275276 self.save()277 }278279 280 281 282 283 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {284 self.check_is_internal()?;285 ensure!(286 self.collection.sponsorship.pending_sponsor() == Some(sender),287 Error::<T>::ConfirmSponsorshipFail288 );289290 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());291292 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));293 <PalletEvm<T>>::deposit_log(294 erc::CollectionHelpersEvents::CollectionChanged {295 collection_id: eth::collection_id_to_address(self.id),296 }297 .to_log(T::ContractAddress::get()),298 );299300 self.save()301 }302303 304 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {305 self.check_is_internal()?;306 self.check_is_owner_or_admin(sender)?;307308 self.collection.sponsorship = SponsorshipState::Disabled;309310 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));311 <PalletEvm<T>>::deposit_log(312 erc::CollectionHelpersEvents::CollectionChanged {313 collection_id: eth::collection_id_to_address(self.id),314 }315 .to_log(T::ContractAddress::get()),316 );317 self.save()318 }319320 321 322 323 324 pub fn force_remove_sponsor(&mut self) -> DispatchResult {325 self.check_is_internal()?;326327 self.collection.sponsorship = SponsorshipState::Disabled;328329 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));330 <PalletEvm<T>>::deposit_log(331 erc::CollectionHelpersEvents::CollectionChanged {332 collection_id: eth::collection_id_to_address(self.id),333 }334 .to_log(T::ContractAddress::get()),335 );336 self.save()337 }338339 340 341 pub fn check_is_internal(&self) -> DispatchResult {342 if self.flags.external {343 return Err(<Error<T>>::CollectionIsExternal)?;344 }345346 Ok(())347 }348349 350 351 pub fn check_is_external(&self) -> DispatchResult {352 if !self.flags.external {353 return Err(<Error<T>>::CollectionIsInternal)?;354 }355356 Ok(())357 }358}359360impl<T: Config> Deref for CollectionHandle<T> {361 type Target = Collection<T::AccountId>;362363 fn deref(&self) -> &Self::Target {364 &self.collection365 }366}367368impl<T: Config> DerefMut for CollectionHandle<T> {369 fn deref_mut(&mut self) -> &mut Self::Target {370 &mut self.collection371 }372}373374impl<T: Config> CollectionHandle<T> {375 376 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {377 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);378 Ok(())379 }380381 382 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {383 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))384 }385386 387 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {388 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);389 Ok(())390 }391392 393 pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {394 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)395 }396397 398 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {399 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)400 }401402 403 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {404 ensure!(405 <Allowlist<T>>::get((self.id, user)),406 <Error<T>>::AddressNotInAllowlist407 );408 Ok(())409 }410411 412 413 414 pub fn change_owner(415 &mut self,416 caller: T::CrossAccountId,417 new_owner: T::CrossAccountId,418 ) -> DispatchResult {419 self.check_is_internal()?;420 self.check_is_owner(&caller)?;421 self.collection.owner = new_owner.as_sub().clone();422423 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(424 self.id,425 new_owner.as_sub().clone(),426 ));427 <PalletEvm<T>>::deposit_log(428 erc::CollectionHelpersEvents::CollectionChanged {429 collection_id: eth::collection_id_to_address(self.id),430 }431 .to_log(T::ContractAddress::get()),432 );433434 self.save()435 }436}437438#[frame_support::pallet]439pub mod pallet {440 use super::*;441 use dispatch::CollectionDispatch;442 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};443 use frame_system::pallet_prelude::*;444 use frame_support::traits::Currency;445 use up_data_structs::{TokenId, mapping::TokenAddressMapping};446 use scale_info::TypeInfo;447 use weights::WeightInfo;448449 #[pallet::config]450 pub trait Config:451 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo452 {453 454 type WeightInfo: WeightInfo;455456 457 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;458459 460 type Currency: Currency<Self::AccountId>;461462 463 #[pallet::constant]464 type CollectionCreationPrice: Get<465 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,466 >;467468 469 type CollectionDispatch: CollectionDispatch<Self>;470471 472 type TreasuryAccountId: Get<Self::AccountId>;473474 475 #[pallet::constant]476 type ContractAddress: Get<H160>;477478 479 type EvmTokenAddressMapping: TokenAddressMapping<H160>;480481 482 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;483 }484485 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);486487 #[pallet::pallet]488 #[pallet::storage_version(STORAGE_VERSION)]489 #[pallet::generate_store(pub(super) trait Store)]490 pub struct Pallet<T>(_);491492 #[pallet::extra_constants]493 impl<T: Config> Pallet<T> {494 495 pub fn collection_admins_limit() -> u32 {496 COLLECTION_ADMINS_LIMIT497 }498 }499500 #[pallet::event]501 #[pallet::generate_deposit(pub fn deposit_event)]502 pub enum Event<T: Config> {503 504 CollectionCreated(505 506 CollectionId,507 508 u8,509 510 T::AccountId,511 ),512513 514 CollectionDestroyed(515 516 CollectionId,517 ),518519 520 ItemCreated(521 522 CollectionId,523 524 TokenId,525 526 T::CrossAccountId,527 528 u128,529 ),530531 532 ItemDestroyed(533 534 CollectionId,535 536 TokenId,537 538 T::CrossAccountId,539 540 u128,541 ),542543 544 Transfer(545 546 CollectionId,547 548 TokenId,549 550 T::CrossAccountId,551 552 T::CrossAccountId,553 554 u128,555 ),556557 558 Approved(559 560 CollectionId,561 562 TokenId,563 564 T::CrossAccountId,565 566 T::CrossAccountId,567 568 u128,569 ),570571 572 ApprovedForAll(573 574 CollectionId,575 576 T::CrossAccountId,577 578 T::CrossAccountId,579 580 bool,581 ),582583 584 CollectionPropertySet(585 586 CollectionId,587 588 PropertyKey,589 ),590591 592 CollectionPropertyDeleted(593 594 CollectionId,595 596 PropertyKey,597 ),598599 600 TokenPropertySet(601 602 CollectionId,603 604 TokenId,605 606 PropertyKey,607 ),608609 610 TokenPropertyDeleted(611 612 CollectionId,613 614 TokenId,615 616 PropertyKey,617 ),618619 620 PropertyPermissionSet(621 622 CollectionId,623 624 PropertyKey,625 ),626627 628 AllowListAddressAdded(629 630 CollectionId,631 632 T::CrossAccountId,633 ),634635 636 AllowListAddressRemoved(637 638 CollectionId,639 640 T::CrossAccountId,641 ),642643 644 CollectionAdminAdded(645 646 CollectionId,647 648 T::CrossAccountId,649 ),650651 652 CollectionAdminRemoved(653 654 CollectionId,655 656 T::CrossAccountId,657 ),658659 660 CollectionLimitSet(661 662 CollectionId,663 ),664665 666 CollectionOwnerChanged(667 668 CollectionId,669 670 T::AccountId,671 ),672673 674 CollectionPermissionSet(675 676 CollectionId,677 ),678679 680 CollectionSponsorSet(681 682 CollectionId,683 684 T::AccountId,685 ),686687 688 SponsorshipConfirmed(689 690 CollectionId,691 692 T::AccountId,693 ),694695 696 CollectionSponsorRemoved(697 698 CollectionId,699 ),700 }701702 #[pallet::error]703 pub enum Error<T> {704 705 CollectionNotFound,706 707 MustBeTokenOwner,708 709 NoPermission,710 711 CantDestroyNotEmptyCollection,712 713 PublicMintingNotAllowed,714 715 AddressNotInAllowlist,716717 718 CollectionNameLimitExceeded,719 720 CollectionDescriptionLimitExceeded,721 722 CollectionTokenPrefixLimitExceeded,723 724 TotalCollectionsLimitExceeded,725 726 CollectionAdminCountExceeded,727 728 CollectionLimitBoundsExceeded,729 730 OwnerPermissionsCantBeReverted,731 732 TransferNotAllowed,733 734 AccountTokenLimitExceeded,735 736 CollectionTokenLimitExceeded,737 738 MetadataFlagFrozen,739740 741 TokenNotFound,742 743 TokenValueTooLow,744 745 ApprovedValueTooLow,746 747 CantApproveMoreThanOwned,748749 750 AddressIsZero,751752 753 UnsupportedOperation,754755 756 NotSufficientFounds,757758 759 UserIsNotAllowedToNest,760 761 SourceCollectionIsNotAllowedToNest,762763 764 CollectionFieldSizeExceeded,765766 767 NoSpaceForProperty,768769 770 PropertyLimitReached,771772 773 PropertyKeyIsTooLong,774775 776 InvalidCharacterInPropertyKey,777778 779 EmptyPropertyKey,780781 782 CollectionIsExternal,783784 785 CollectionIsInternal,786787 788 ConfirmSponsorshipFail,789790 791 UserIsNotCollectionAdmin,792 }793794 795 #[pallet::storage]796 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;797798 799 #[pallet::storage]800 pub type DestroyedCollectionCount<T> =801 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;802803 804 #[pallet::storage]805 pub type CollectionById<T> = StorageMap<806 Hasher = Blake2_128Concat,807 Key = CollectionId,808 Value = Collection<<T as frame_system::Config>::AccountId>,809 QueryKind = OptionQuery,810 >;811812 813 #[pallet::storage]814 #[pallet::getter(fn collection_properties)]815 pub type CollectionProperties<T> = StorageMap<816 Hasher = Blake2_128Concat,817 Key = CollectionId,818 Value = Properties,819 QueryKind = ValueQuery,820 OnEmpty = up_data_structs::CollectionProperties,821 >;822823 824 #[pallet::storage]825 #[pallet::getter(fn property_permissions)]826 pub type CollectionPropertyPermissions<T> = StorageMap<827 Hasher = Blake2_128Concat,828 Key = CollectionId,829 Value = PropertiesPermissionMap,830 QueryKind = ValueQuery,831 >;832833 834 #[pallet::storage]835 pub type AdminAmount<T> = StorageMap<836 Hasher = Blake2_128Concat,837 Key = CollectionId,838 Value = u32,839 QueryKind = ValueQuery,840 >;841842 843 #[pallet::storage]844 pub type IsAdmin<T: Config> = StorageNMap<845 Key = (846 Key<Blake2_128Concat, CollectionId>,847 Key<Blake2_128Concat, T::CrossAccountId>,848 ),849 Value = bool,850 QueryKind = ValueQuery,851 >;852853 854 #[pallet::storage]855 pub type Allowlist<T: Config> = StorageNMap<856 Key = (857 Key<Blake2_128Concat, CollectionId>,858 Key<Blake2_128Concat, T::CrossAccountId>,859 ),860 Value = bool,861 QueryKind = ValueQuery,862 >;863864 865 #[pallet::storage]866 pub type DummyStorageValue<T: Config> = StorageValue<867 Value = (868 CollectionStats,869 CollectionId,870 TokenId,871 TokenChild,872 PhantomType<(873 TokenData<T::CrossAccountId>,874 RpcCollection<T::AccountId>,875 876 RmrkCollectionInfo<T::AccountId>,877 RmrkInstanceInfo<T::AccountId>,878 RmrkResourceInfo,879 RmrkPropertyInfo,880 RmrkBaseInfo<T::AccountId>,881 RmrkPartType,882 RmrkBoundedTheme,883 RmrkNftChild,884 )>,885 ),886 QueryKind = OptionQuery,887 >;888889 #[pallet::hooks]890 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {891 fn on_runtime_upgrade() -> Weight {892 StorageVersion::new(1).put::<Pallet<T>>();893894 Weight::zero()895 }896 }897}898899impl<T: Config> Pallet<T> {900 901 902 903 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {904 ensure!(905 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,906 <Error<T>>::AddressIsZero907 );908 Ok(())909 }910911 912 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {913 <IsAdmin<T>>::iter_prefix((collection,))914 .map(|(a, _)| a)915 .collect()916 }917918 919 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {920 <Allowlist<T>>::iter_prefix((collection,))921 .map(|(a, _)| a)922 .collect()923 }924925 926 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {927 <Allowlist<T>>::get((collection, user))928 }929930 931 pub fn collection_stats() -> CollectionStats {932 let created = <CreatedCollectionCount<T>>::get();933 let destroyed = <DestroyedCollectionCount<T>>::get();934 CollectionStats {935 created: created.0,936 destroyed: destroyed.0,937 alive: created.0 - destroyed.0,938 }939 }940941 942 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {943 let collection = <CollectionById<T>>::get(collection)?;944 let limits = collection.limits;945 let effective_limits = CollectionLimits {946 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),947 sponsored_data_size: Some(limits.sponsored_data_size()),948 sponsored_data_rate_limit: Some(949 limits950 .sponsored_data_rate_limit951 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),952 ),953 token_limit: Some(limits.token_limit()),954 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(955 match collection.mode {956 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,957 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,958 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,959 },960 )),961 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),962 owner_can_transfer: Some(limits.owner_can_transfer()),963 owner_can_destroy: Some(limits.owner_can_destroy()),964 transfers_enabled: Some(limits.transfers_enabled()),965 };966967 Some(effective_limits)968 }969970 971 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {972 let Collection {973 name,974 description,975 owner,976 mode,977 token_prefix,978 sponsorship,979 limits,980 permissions,981 flags,982 } = <CollectionById<T>>::get(collection)?;983984 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)985 .into_iter()986 .map(|(key, permission)| PropertyKeyPermission { key, permission })987 .collect();988989 let properties = <CollectionProperties<T>>::get(collection)990 .into_iter()991 .map(|(key, value)| Property { key, value })992 .collect();993994 let permissions = CollectionPermissions {995 access: Some(permissions.access()),996 mint_mode: Some(permissions.mint_mode()),997 nesting: Some(permissions.nesting().clone()),998 };9991000 Some(RpcCollection {1001 name: name.into_inner(),1002 description: description.into_inner(),1003 owner,1004 mode,1005 token_prefix: token_prefix.into_inner(),1006 sponsorship,1007 limits,1008 permissions,1009 token_property_permissions,1010 properties,1011 read_only: flags.external,10121013 flags: RpcCollectionFlags {1014 foreign: flags.foreign,1015 erc721metadata: flags.erc721metadata,1016 },1017 })1018 }1019}10201021macro_rules! limit_default {1022 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1023 $(1024 if let Some($new) = $new.$field {1025 let $old = $old.$field($($arg)?);1026 let _ = $new;1027 let _ = $old;1028 $check1029 } else {1030 $new.$field = $old.$field1031 }1032 )*1033 }};1034}1035macro_rules! limit_default_clone {1036 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1037 $(1038 if let Some($new) = $new.$field.clone() {1039 let $old = $old.$field($($arg)?);1040 let _ = $new;1041 let _ = $old;1042 $check1043 } else {1044 $new.$field = $old.$field.clone()1045 }1046 )*1047 }};1048}10491050impl<T: Config> Pallet<T> {1051 1052 1053 1054 1055 1056 pub fn init_collection(1057 owner: T::CrossAccountId,1058 payer: T::CrossAccountId,1059 data: CreateCollectionData<T::AccountId>,1060 flags: CollectionFlags,1061 ) -> Result<CollectionId, DispatchError> {1062 {1063 ensure!(1064 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1065 Error::<T>::CollectionTokenPrefixLimitExceeded1066 );1067 }10681069 let created_count = <CreatedCollectionCount<T>>::get()1070 .01071 .checked_add(1)1072 .ok_or(ArithmeticError::Overflow)?;1073 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1074 let id = CollectionId(created_count);10751076 1077 ensure!(1078 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1079 <Error<T>>::TotalCollectionsLimitExceeded1080 );10811082 10831084 let collection = Collection {1085 owner: owner.as_sub().clone(),1086 name: data.name,1087 mode: data.mode.clone(),1088 description: data.description,1089 token_prefix: data.token_prefix,1090 sponsorship: data1091 .pending_sponsor1092 .map(SponsorshipState::Unconfirmed)1093 .unwrap_or_default(),1094 limits: data1095 .limits1096 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1097 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1098 permissions: data1099 .permissions1100 .map(|permissions| {1101 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1102 })1103 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1104 flags,1105 };11061107 let mut collection_properties = up_data_structs::CollectionProperties::get();1108 collection_properties1109 .try_set_from_iter(data.properties.into_iter())1110 .map_err(<Error<T>>::from)?;11111112 CollectionProperties::<T>::insert(id, collection_properties);11131114 let mut token_props_permissions = PropertiesPermissionMap::new();1115 token_props_permissions1116 .try_set_from_iter(data.token_property_permissions.into_iter())1117 .map_err(<Error<T>>::from)?;11181119 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11201121 1122 {1123 let mut imbalance =1124 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1125 imbalance.subsume(1126 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1127 &T::TreasuryAccountId::get(),1128 T::CollectionCreationPrice::get(),1129 ),1130 );1131 <T as Config>::Currency::settle(1132 payer.as_sub(),1133 imbalance,1134 WithdrawReasons::TRANSFER,1135 ExistenceRequirement::KeepAlive,1136 )1137 .map_err(|_| Error::<T>::NotSufficientFounds)?;1138 }11391140 <CreatedCollectionCount<T>>::put(created_count);1141 <Pallet<T>>::deposit_event(Event::CollectionCreated(1142 id,1143 data.mode.id(),1144 owner.as_sub().clone(),1145 ));1146 <PalletEvm<T>>::deposit_log(1147 erc::CollectionHelpersEvents::CollectionCreated {1148 owner: *owner.as_eth(),1149 collection_id: eth::collection_id_to_address(id),1150 }1151 .to_log(T::ContractAddress::get()),1152 );1153 <CollectionById<T>>::insert(id, collection);1154 Ok(id)1155 }11561157 1158 1159 1160 1161 pub fn destroy_collection(1162 collection: CollectionHandle<T>,1163 sender: &T::CrossAccountId,1164 ) -> DispatchResult {1165 ensure!(1166 collection.limits.owner_can_destroy(),1167 <Error<T>>::NoPermission,1168 );1169 collection.check_is_owner(sender)?;11701171 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1172 .01173 .checked_add(1)1174 .ok_or(ArithmeticError::Overflow)?;11751176 11771178 <DestroyedCollectionCount<T>>::put(destroyed_collections);1179 <CollectionById<T>>::remove(collection.id);1180 <AdminAmount<T>>::remove(collection.id);1181 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1182 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1183 <CollectionProperties<T>>::remove(collection.id);11841185 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11861187 <PalletEvm<T>>::deposit_log(1188 erc::CollectionHelpersEvents::CollectionDestroyed {1189 collection_id: eth::collection_id_to_address(collection.id),1190 }1191 .to_log(T::ContractAddress::get()),1192 );1193 Ok(())1194 }11951196 1197 1198 1199 1200 1201 pub fn set_collection_property(1202 collection: &CollectionHandle<T>,1203 sender: &T::CrossAccountId,1204 property: Property,1205 ) -> DispatchResult {1206 collection.check_is_owner_or_admin(sender)?;12071208 CollectionProperties::<T>::try_mutate(collection.id, |properties| {1209 let property = property.clone();1210 properties.try_set(property.key, property.value)1211 })1212 .map_err(<Error<T>>::from)?;12131214 Self::deposit_event(Event::CollectionPropertySet(collection.id, property.key));1215 <PalletEvm<T>>::deposit_log(1216 erc::CollectionHelpersEvents::CollectionChanged {1217 collection_id: eth::collection_id_to_address(collection.id),1218 }1219 .to_log(T::ContractAddress::get()),1220 );12211222 Ok(())1223 }12241225 1226 1227 1228 1229 1230 1231 pub fn set_scoped_collection_property(1232 collection_id: CollectionId,1233 scope: PropertyScope,1234 property: Property,1235 ) -> DispatchResult {1236 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1237 properties.try_scoped_set(scope, property.key, property.value)1238 })1239 .map_err(<Error<T>>::from)?;12401241 Ok(())1242 }12431244 1245 1246 1247 1248 1249 1250 pub fn set_scoped_collection_properties(1251 collection_id: CollectionId,1252 scope: PropertyScope,1253 properties: impl Iterator<Item = Property>,1254 ) -> DispatchResult {1255 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1256 stored_properties.try_scoped_set_from_iter(scope, properties)1257 })1258 .map_err(<Error<T>>::from)?;12591260 Ok(())1261 }12621263 1264 1265 1266 1267 1268 #[transactional]1269 pub fn set_collection_properties(1270 collection: &CollectionHandle<T>,1271 sender: &T::CrossAccountId,1272 properties: Vec<Property>,1273 ) -> DispatchResult {1274 for property in properties {1275 Self::set_collection_property(collection, sender, property)?;1276 }12771278 Ok(())1279 }12801281 1282 1283 1284 1285 1286 pub fn delete_collection_property(1287 collection: &CollectionHandle<T>,1288 sender: &T::CrossAccountId,1289 property_key: PropertyKey,1290 ) -> DispatchResult {1291 collection.check_is_owner_or_admin(sender)?;12921293 CollectionProperties::<T>::try_mutate(collection.id, |properties| {1294 properties.remove(&property_key)1295 })1296 .map_err(<Error<T>>::from)?;12971298 Self::deposit_event(Event::CollectionPropertyDeleted(1299 collection.id,1300 property_key,1301 ));1302 <PalletEvm<T>>::deposit_log(1303 erc::CollectionHelpersEvents::CollectionChanged {1304 collection_id: eth::collection_id_to_address(collection.id),1305 }1306 .to_log(T::ContractAddress::get()),1307 );13081309 Ok(())1310 }13111312 1313 1314 1315 1316 1317 #[transactional]1318 pub fn delete_collection_properties(1319 collection: &CollectionHandle<T>,1320 sender: &T::CrossAccountId,1321 property_keys: Vec<PropertyKey>,1322 ) -> DispatchResult {1323 for key in property_keys {1324 Self::delete_collection_property(collection, sender, key)?;1325 }13261327 Ok(())1328 }13291330 1331 1332 1333 1334 1335 1336 pub fn set_property_permission_unchecked(1337 collection: CollectionId,1338 property_permission: PropertyKeyPermission,1339 ) -> DispatchResult {1340 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1341 permissions.try_set(property_permission.key, property_permission.permission)1342 })1343 .map_err(<Error<T>>::from)?;1344 Ok(())1345 }13461347 1348 1349 1350 1351 1352 pub fn set_property_permission(1353 collection: &CollectionHandle<T>,1354 sender: &T::CrossAccountId,1355 property_permission: PropertyKeyPermission,1356 ) -> DispatchResult {1357 Self::set_scoped_property_permission(1358 collection,1359 sender,1360 PropertyScope::None,1361 property_permission,1362 )1363 }13641365 1366 1367 1368 1369 1370 1371 pub fn set_scoped_property_permission(1372 collection: &CollectionHandle<T>,1373 sender: &T::CrossAccountId,1374 scope: PropertyScope,1375 property_permission: PropertyKeyPermission,1376 ) -> DispatchResult {1377 collection.check_is_owner_or_admin(sender)?;13781379 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1380 let current_permission = all_permissions.get(&property_permission.key);1381 if matches![1382 current_permission,1383 Some(PropertyPermission { mutable: false, .. })1384 ] {1385 return Err(<Error<T>>::NoPermission.into());1386 }13871388 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1389 let property_permission = property_permission.clone();1390 permissions.try_scoped_set(1391 scope,1392 property_permission.key,1393 property_permission.permission,1394 )1395 })1396 .map_err(<Error<T>>::from)?;13971398 Self::deposit_event(Event::PropertyPermissionSet(1399 collection.id,1400 property_permission.key,1401 ));1402 <PalletEvm<T>>::deposit_log(1403 erc::CollectionHelpersEvents::CollectionChanged {1404 collection_id: eth::collection_id_to_address(collection.id),1405 }1406 .to_log(T::ContractAddress::get()),1407 );14081409 Ok(())1410 }14111412 1413 1414 1415 1416 1417 #[transactional]1418 pub fn set_token_property_permissions(1419 collection: &CollectionHandle<T>,1420 sender: &T::CrossAccountId,1421 property_permissions: Vec<PropertyKeyPermission>,1422 ) -> DispatchResult {1423 Self::set_scoped_token_property_permissions(1424 collection,1425 sender,1426 PropertyScope::None,1427 property_permissions,1428 )1429 }14301431 1432 1433 1434 1435 1436 1437 #[transactional]1438 pub fn set_scoped_token_property_permissions(1439 collection: &CollectionHandle<T>,1440 sender: &T::CrossAccountId,1441 scope: PropertyScope,1442 property_permissions: Vec<PropertyKeyPermission>,1443 ) -> DispatchResult {1444 for prop_pemission in property_permissions {1445 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1446 }14471448 Ok(())1449 }14501451 1452 pub fn get_collection_property(1453 collection_id: CollectionId,1454 key: &PropertyKey,1455 ) -> Option<PropertyValue> {1456 Self::collection_properties(collection_id).get(key).cloned()1457 }14581459 1460 pub fn bytes_keys_to_property_keys(1461 keys: Vec<Vec<u8>>,1462 ) -> Result<Vec<PropertyKey>, DispatchError> {1463 keys.into_iter()1464 .map(|key| -> Result<PropertyKey, DispatchError> {1465 key.try_into()1466 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1467 })1468 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1469 }14701471 1472 pub fn filter_collection_properties(1473 collection_id: CollectionId,1474 keys: Option<Vec<PropertyKey>>,1475 ) -> Result<Vec<Property>, DispatchError> {1476 let properties = Self::collection_properties(collection_id);14771478 let properties = keys1479 .map(|keys| {1480 keys.into_iter()1481 .filter_map(|key| {1482 properties.get(&key).map(|value| Property {1483 key,1484 value: value.clone(),1485 })1486 })1487 .collect()1488 })1489 .unwrap_or_else(|| {1490 properties1491 .into_iter()1492 .map(|(key, value)| Property { key, value })1493 .collect()1494 });14951496 Ok(properties)1497 }14981499 1500 pub fn filter_property_permissions(1501 collection_id: CollectionId,1502 keys: Option<Vec<PropertyKey>>,1503 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1504 let permissions = Self::property_permissions(collection_id);15051506 let key_permissions = keys1507 .map(|keys| {1508 keys.into_iter()1509 .filter_map(|key| {1510 permissions1511 .get(&key)1512 .map(|permission| PropertyKeyPermission {1513 key,1514 permission: permission.clone(),1515 })1516 })1517 .collect()1518 })1519 .unwrap_or_else(|| {1520 permissions1521 .into_iter()1522 .map(|(key, permission)| PropertyKeyPermission { key, permission })1523 .collect()1524 });15251526 Ok(key_permissions)1527 }15281529 1530 1531 1532 pub fn toggle_allowlist(1533 collection: &CollectionHandle<T>,1534 sender: &T::CrossAccountId,1535 user: &T::CrossAccountId,1536 allowed: bool,1537 ) -> DispatchResult {1538 collection.check_is_owner_or_admin(sender)?;15391540 15411542 if allowed {1543 <Allowlist<T>>::insert((collection.id, user), true);1544 Self::deposit_event(Event::<T>::AllowListAddressAdded(1545 collection.id,1546 user.clone(),1547 ));1548 } else {1549 <Allowlist<T>>::remove((collection.id, user));1550 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1551 collection.id,1552 user.clone(),1553 ));1554 }15551556 <PalletEvm<T>>::deposit_log(1557 erc::CollectionHelpersEvents::CollectionChanged {1558 collection_id: eth::collection_id_to_address(collection.id),1559 }1560 .to_log(T::ContractAddress::get()),1561 );15621563 Ok(())1564 }15651566 1567 1568 1569 pub fn toggle_admin(1570 collection: &CollectionHandle<T>,1571 sender: &T::CrossAccountId,1572 user: &T::CrossAccountId,1573 admin: bool,1574 ) -> DispatchResult {1575 collection.check_is_internal()?;1576 collection.check_is_owner(sender)?;15771578 let is_admin = <IsAdmin<T>>::get((collection.id, user));1579 if is_admin == admin {1580 if admin {1581 return Ok(());1582 } else {1583 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1584 }1585 }1586 let amount = <AdminAmount<T>>::get(collection.id);15871588 15891590 if admin {1591 let amount = amount1592 .checked_add(1)1593 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1594 ensure!(1595 amount <= Self::collection_admins_limit(),1596 <Error<T>>::CollectionAdminCountExceeded,1597 );15981599 <AdminAmount<T>>::insert(collection.id, amount);1600 <IsAdmin<T>>::insert((collection.id, user), true);16011602 Self::deposit_event(Event::<T>::CollectionAdminAdded(1603 collection.id,1604 user.clone(),1605 ));1606 } else {1607 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1608 <IsAdmin<T>>::remove((collection.id, user));16091610 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1611 collection.id,1612 user.clone(),1613 ));1614 }16151616 <PalletEvm<T>>::deposit_log(1617 erc::CollectionHelpersEvents::CollectionChanged {1618 collection_id: eth::collection_id_to_address(collection.id),1619 }1620 .to_log(T::ContractAddress::get()),1621 );16221623 Ok(())1624 }16251626 1627 pub fn update_limits(1628 user: &T::CrossAccountId,1629 collection: &mut CollectionHandle<T>,1630 new_limit: CollectionLimits,1631 ) -> DispatchResult {1632 collection.check_is_internal()?;1633 collection.check_is_owner_or_admin(user)?;16341635 collection.limits =1636 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16371638 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1639 <PalletEvm<T>>::deposit_log(1640 erc::CollectionHelpersEvents::CollectionChanged {1641 collection_id: eth::collection_id_to_address(collection.id),1642 }1643 .to_log(T::ContractAddress::get()),1644 );16451646 collection.save()1647 }16481649 1650 fn clamp_limits(1651 mode: CollectionMode,1652 old_limit: &CollectionLimits,1653 mut new_limit: CollectionLimits,1654 ) -> Result<CollectionLimits, DispatchError> {1655 let limits = old_limit;1656 limit_default!(old_limit, new_limit,1657 account_token_ownership_limit => ensure!(1658 new_limit <= MAX_TOKEN_OWNERSHIP,1659 <Error<T>>::CollectionLimitBoundsExceeded,1660 ),1661 sponsored_data_size => ensure!(1662 new_limit <= CUSTOM_DATA_LIMIT,1663 <Error<T>>::CollectionLimitBoundsExceeded,1664 ),16651666 sponsored_data_rate_limit => {},1667 token_limit => ensure!(1668 old_limit >= new_limit && new_limit > 0,1669 <Error<T>>::CollectionTokenLimitExceeded1670 ),16711672 sponsor_transfer_timeout(match mode {1673 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1674 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1675 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1676 }) => ensure!(1677 new_limit <= MAX_SPONSOR_TIMEOUT,1678 <Error<T>>::CollectionLimitBoundsExceeded,1679 ),1680 sponsor_approve_timeout => {},1681 owner_can_transfer => ensure!(1682 !limits.owner_can_transfer_instaled() ||1683 old_limit || !new_limit,1684 <Error<T>>::OwnerPermissionsCantBeReverted,1685 ),1686 owner_can_destroy => ensure!(1687 old_limit || !new_limit,1688 <Error<T>>::OwnerPermissionsCantBeReverted,1689 ),1690 transfers_enabled => {},1691 );1692 Ok(new_limit)1693 }16941695 1696 pub fn update_permissions(1697 user: &T::CrossAccountId,1698 collection: &mut CollectionHandle<T>,1699 new_permission: CollectionPermissions,1700 ) -> DispatchResult {1701 collection.check_is_internal()?;1702 collection.check_is_owner_or_admin(user)?;1703 collection.permissions = Self::clamp_permissions(1704 collection.mode.clone(),1705 &collection.permissions,1706 new_permission,1707 )?;17081709 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1710 <PalletEvm<T>>::deposit_log(1711 erc::CollectionHelpersEvents::CollectionChanged {1712 collection_id: eth::collection_id_to_address(collection.id),1713 }1714 .to_log(T::ContractAddress::get()),1715 );17161717 collection.save()1718 }17191720 1721 fn clamp_permissions(1722 _mode: CollectionMode,1723 old_permission: &CollectionPermissions,1724 mut new_permission: CollectionPermissions,1725 ) -> Result<CollectionPermissions, DispatchError> {1726 limit_default_clone!(old_permission, new_permission,1727 access => {},1728 mint_mode => {},1729 nesting => { },1730 );1731 Ok(new_permission)1732 }1733}173417351736#[macro_export]1737macro_rules! unsupported {1738 ($runtime:path) => {1739 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1740 };1741}174217431744pub trait CommonWeightInfo<CrossAccountId> {1745 1746 fn create_item() -> Weight;17471748 1749 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17501751 1752 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17531754 1755 fn burn_item() -> Weight;17561757 1758 1759 1760 fn set_collection_properties(amount: u32) -> Weight;17611762 1763 1764 1765 fn delete_collection_properties(amount: u32) -> Weight;17661767 1768 1769 1770 fn set_token_properties(amount: u32) -> Weight;17711772 1773 1774 1775 fn delete_token_properties(amount: u32) -> Weight;17761777 1778 1779 1780 fn set_token_property_permissions(amount: u32) -> Weight;17811782 1783 fn transfer() -> Weight;17841785 1786 fn approve() -> Weight;17871788 1789 fn transfer_from() -> Weight;17901791 1792 fn burn_from() -> Weight;17931794 1795 1796 1797 1798 fn burn_recursively_self_raw() -> Weight;17991800 1801 1802 1803 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18041805 1806 1807 1808 1809 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1810 Self::burn_recursively_self_raw()1811 .saturating_mul(max_selfs.max(1) as u64)1812 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1813 }18141815 1816 fn token_owner() -> Weight;18171818 1819 fn set_allowance_for_all() -> Weight;18201821 1822 fn repair_item() -> Weight;1823}182418251826pub trait RefungibleExtensionsWeightInfo {1827 1828 fn repartition() -> Weight;1829}183018311832183318341835pub trait CommonCollectionOperations<T: Config> {1836 1837 1838 1839 1840 1841 1842 fn create_item(1843 &self,1844 sender: T::CrossAccountId,1845 to: T::CrossAccountId,1846 data: CreateItemData,1847 nesting_budget: &dyn Budget,1848 ) -> DispatchResultWithPostInfo;18491850 1851 1852 1853 1854 1855 1856 fn create_multiple_items(1857 &self,1858 sender: T::CrossAccountId,1859 to: T::CrossAccountId,1860 data: Vec<CreateItemData>,1861 nesting_budget: &dyn Budget,1862 ) -> DispatchResultWithPostInfo;18631864 1865 1866 1867 1868 1869 1870 fn create_multiple_items_ex(1871 &self,1872 sender: T::CrossAccountId,1873 data: CreateItemExData<T::CrossAccountId>,1874 nesting_budget: &dyn Budget,1875 ) -> DispatchResultWithPostInfo;18761877 1878 1879 1880 1881 1882 fn burn_item(1883 &self,1884 sender: T::CrossAccountId,1885 token: TokenId,1886 amount: u128,1887 ) -> DispatchResultWithPostInfo;18881889 1890 1891 1892 1893 1894 1895 fn burn_item_recursively(1896 &self,1897 sender: T::CrossAccountId,1898 token: TokenId,1899 self_budget: &dyn Budget,1900 breadth_budget: &dyn Budget,1901 ) -> DispatchResultWithPostInfo;19021903 1904 1905 1906 1907 fn set_collection_properties(1908 &self,1909 sender: T::CrossAccountId,1910 properties: Vec<Property>,1911 ) -> DispatchResultWithPostInfo;19121913 1914 1915 1916 1917 fn delete_collection_properties(1918 &self,1919 sender: &T::CrossAccountId,1920 property_keys: Vec<PropertyKey>,1921 ) -> DispatchResultWithPostInfo;19221923 1924 1925 1926 1927 1928 1929 1930 1931 1932 fn set_token_properties(1933 &self,1934 sender: T::CrossAccountId,1935 token_id: TokenId,1936 properties: Vec<Property>,1937 budget: &dyn Budget,1938 ) -> DispatchResultWithPostInfo;19391940 1941 1942 1943 1944 1945 1946 1947 1948 1949 fn delete_token_properties(1950 &self,1951 sender: T::CrossAccountId,1952 token_id: TokenId,1953 property_keys: Vec<PropertyKey>,1954 budget: &dyn Budget,1955 ) -> DispatchResultWithPostInfo;19561957 1958 1959 1960 1961 1962 1963 fn set_token_property_permissions(1964 &self,1965 sender: &T::CrossAccountId,1966 property_permissions: Vec<PropertyKeyPermission>,1967 ) -> DispatchResultWithPostInfo;19681969 1970 1971 1972 1973 1974 1975 1976 fn transfer(1977 &self,1978 sender: T::CrossAccountId,1979 to: T::CrossAccountId,1980 token: TokenId,1981 amount: u128,1982 budget: &dyn Budget,1983 ) -> DispatchResultWithPostInfo;19841985 1986 1987 1988 1989 1990 1991 fn approve(1992 &self,1993 sender: T::CrossAccountId,1994 spender: T::CrossAccountId,1995 token: TokenId,1996 amount: u128,1997 ) -> DispatchResultWithPostInfo;19981999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 fn transfer_from(2010 &self,2011 sender: T::CrossAccountId,2012 from: T::CrossAccountId,2013 to: T::CrossAccountId,2014 token: TokenId,2015 amount: u128,2016 budget: &dyn Budget,2017 ) -> DispatchResultWithPostInfo;20182019 2020 2021 2022 2023 2024 2025 2026 2027 2028 fn burn_from(2029 &self,2030 sender: T::CrossAccountId,2031 from: T::CrossAccountId,2032 token: TokenId,2033 amount: u128,2034 budget: &dyn Budget,2035 ) -> DispatchResultWithPostInfo;20362037 2038 2039 2040 2041 2042 2043 fn check_nesting(2044 &self,2045 sender: T::CrossAccountId,2046 from: (CollectionId, TokenId),2047 under: TokenId,2048 budget: &dyn Budget,2049 ) -> DispatchResult;20502051 2052 2053 2054 2055 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20562057 2058 2059 2060 2061 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20622063 2064 2065 2066 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;20672068 2069 fn collection_tokens(&self) -> Vec<TokenId>;20702071 2072 2073 2074 fn token_exists(&self, token: TokenId) -> bool;20752076 2077 fn last_token_id(&self) -> TokenId;20782079 2080 2081 2082 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;20832084 2085 2086 2087 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;20882089 2090 2091 2092 2093 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;20942095 2096 2097 2098 2099 2100 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21012102 2103 fn total_supply(&self) -> u32;21042105 2106 2107 2108 fn account_balance(&self, account: T::CrossAccountId) -> u32;21092110 2111 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21122113 2114 fn total_pieces(&self, token: TokenId) -> Option<u128>;21152116 2117 2118 2119 2120 2121 fn allowance(2122 &self,2123 sender: T::CrossAccountId,2124 spender: T::CrossAccountId,2125 token: TokenId,2126 ) -> u128;21272128 2129 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21302131 2132 2133 2134 2135 fn set_allowance_for_all(2136 &self,2137 owner: T::CrossAccountId,2138 operator: T::CrossAccountId,2139 approve: bool,2140 ) -> DispatchResultWithPostInfo;21412142 2143 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21442145 2146 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2147}214821492150pub trait RefungibleExtensions<T>2151where2152 T: Config,2153{2154 2155 2156 2157 2158 2159 2160 2161 fn repartition(2162 &self,2163 sender: &T::CrossAccountId,2164 token: TokenId,2165 amount: u128,2166 ) -> DispatchResultWithPostInfo;2167}21682169217021712172pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2173 let post_info = PostDispatchInfo {2174 actual_weight: Some(weight),2175 pays_fee: Pays::Yes,2176 };2177 match res {2178 Ok(()) => Ok(post_info),2179 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2180 }2181}21822183impl<T: Config> From<PropertiesError> for Error<T> {2184 fn from(error: PropertiesError) -> Self {2185 match error {2186 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2187 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2188 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2189 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2190 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2191 }2192 }2193}