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 }17331734 1735 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1736 CollectionProperties::<T>::mutate(collection_id, |properties| {1737 properties.recompute_consumed_space();1738 });17391740 Ok(())1741 }1742}174317441745#[macro_export]1746macro_rules! unsupported {1747 ($runtime:path) => {1748 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1749 };1750}175117521753pub trait CommonWeightInfo<CrossAccountId> {1754 1755 fn create_item() -> Weight;17561757 1758 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17591760 1761 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17621763 1764 fn burn_item() -> Weight;17651766 1767 1768 1769 fn set_collection_properties(amount: u32) -> Weight;17701771 1772 1773 1774 fn delete_collection_properties(amount: u32) -> Weight;17751776 1777 1778 1779 fn set_token_properties(amount: u32) -> Weight;17801781 1782 1783 1784 fn delete_token_properties(amount: u32) -> Weight;17851786 1787 1788 1789 fn set_token_property_permissions(amount: u32) -> Weight;17901791 1792 fn transfer() -> Weight;17931794 1795 fn approve() -> Weight;17961797 1798 fn transfer_from() -> Weight;17991800 1801 fn burn_from() -> Weight;18021803 1804 1805 1806 1807 fn burn_recursively_self_raw() -> Weight;18081809 1810 1811 1812 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18131814 1815 1816 1817 1818 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1819 Self::burn_recursively_self_raw()1820 .saturating_mul(max_selfs.max(1) as u64)1821 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1822 }18231824 1825 fn token_owner() -> Weight;18261827 1828 fn set_allowance_for_all() -> Weight;18291830 1831 fn force_repair_item() -> Weight;1832}183318341835pub trait RefungibleExtensionsWeightInfo {1836 1837 fn repartition() -> Weight;1838}183918401841184218431844pub trait CommonCollectionOperations<T: Config> {1845 1846 1847 1848 1849 1850 1851 fn create_item(1852 &self,1853 sender: T::CrossAccountId,1854 to: T::CrossAccountId,1855 data: CreateItemData,1856 nesting_budget: &dyn Budget,1857 ) -> DispatchResultWithPostInfo;18581859 1860 1861 1862 1863 1864 1865 fn create_multiple_items(1866 &self,1867 sender: T::CrossAccountId,1868 to: T::CrossAccountId,1869 data: Vec<CreateItemData>,1870 nesting_budget: &dyn Budget,1871 ) -> DispatchResultWithPostInfo;18721873 1874 1875 1876 1877 1878 1879 fn create_multiple_items_ex(1880 &self,1881 sender: T::CrossAccountId,1882 data: CreateItemExData<T::CrossAccountId>,1883 nesting_budget: &dyn Budget,1884 ) -> DispatchResultWithPostInfo;18851886 1887 1888 1889 1890 1891 fn burn_item(1892 &self,1893 sender: T::CrossAccountId,1894 token: TokenId,1895 amount: u128,1896 ) -> DispatchResultWithPostInfo;18971898 1899 1900 1901 1902 1903 1904 fn burn_item_recursively(1905 &self,1906 sender: T::CrossAccountId,1907 token: TokenId,1908 self_budget: &dyn Budget,1909 breadth_budget: &dyn Budget,1910 ) -> DispatchResultWithPostInfo;19111912 1913 1914 1915 1916 fn set_collection_properties(1917 &self,1918 sender: T::CrossAccountId,1919 properties: Vec<Property>,1920 ) -> DispatchResultWithPostInfo;19211922 1923 1924 1925 1926 fn delete_collection_properties(1927 &self,1928 sender: &T::CrossAccountId,1929 property_keys: Vec<PropertyKey>,1930 ) -> DispatchResultWithPostInfo;19311932 1933 1934 1935 1936 1937 1938 1939 1940 1941 fn set_token_properties(1942 &self,1943 sender: T::CrossAccountId,1944 token_id: TokenId,1945 properties: Vec<Property>,1946 budget: &dyn Budget,1947 ) -> DispatchResultWithPostInfo;19481949 1950 1951 1952 1953 1954 1955 1956 1957 1958 fn delete_token_properties(1959 &self,1960 sender: T::CrossAccountId,1961 token_id: TokenId,1962 property_keys: Vec<PropertyKey>,1963 budget: &dyn Budget,1964 ) -> DispatchResultWithPostInfo;19651966 1967 1968 1969 1970 1971 1972 fn set_token_property_permissions(1973 &self,1974 sender: &T::CrossAccountId,1975 property_permissions: Vec<PropertyKeyPermission>,1976 ) -> DispatchResultWithPostInfo;19771978 1979 1980 1981 1982 1983 1984 1985 fn transfer(1986 &self,1987 sender: T::CrossAccountId,1988 to: T::CrossAccountId,1989 token: TokenId,1990 amount: u128,1991 budget: &dyn Budget,1992 ) -> DispatchResultWithPostInfo;19931994 1995 1996 1997 1998 1999 2000 fn approve(2001 &self,2002 sender: T::CrossAccountId,2003 spender: T::CrossAccountId,2004 token: TokenId,2005 amount: u128,2006 ) -> DispatchResultWithPostInfo;20072008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 fn transfer_from(2019 &self,2020 sender: T::CrossAccountId,2021 from: T::CrossAccountId,2022 to: T::CrossAccountId,2023 token: TokenId,2024 amount: u128,2025 budget: &dyn Budget,2026 ) -> DispatchResultWithPostInfo;20272028 2029 2030 2031 2032 2033 2034 2035 2036 2037 fn burn_from(2038 &self,2039 sender: T::CrossAccountId,2040 from: T::CrossAccountId,2041 token: TokenId,2042 amount: u128,2043 budget: &dyn Budget,2044 ) -> DispatchResultWithPostInfo;20452046 2047 2048 2049 2050 2051 2052 fn check_nesting(2053 &self,2054 sender: T::CrossAccountId,2055 from: (CollectionId, TokenId),2056 under: TokenId,2057 budget: &dyn Budget,2058 ) -> DispatchResult;20592060 2061 2062 2063 2064 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20652066 2067 2068 2069 2070 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20712072 2073 2074 2075 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;20762077 2078 fn collection_tokens(&self) -> Vec<TokenId>;20792080 2081 2082 2083 fn token_exists(&self, token: TokenId) -> bool;20842085 2086 fn last_token_id(&self) -> TokenId;20872088 2089 2090 2091 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;20922093 2094 2095 2096 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;20972098 2099 2100 2101 2102 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21032104 2105 2106 2107 2108 2109 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21102111 2112 fn total_supply(&self) -> u32;21132114 2115 2116 2117 fn account_balance(&self, account: T::CrossAccountId) -> u32;21182119 2120 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21212122 2123 fn total_pieces(&self, token: TokenId) -> Option<u128>;21242125 2126 2127 2128 2129 2130 fn allowance(2131 &self,2132 sender: T::CrossAccountId,2133 spender: T::CrossAccountId,2134 token: TokenId,2135 ) -> u128;21362137 2138 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21392140 2141 2142 2143 2144 fn set_allowance_for_all(2145 &self,2146 owner: T::CrossAccountId,2147 operator: T::CrossAccountId,2148 approve: bool,2149 ) -> DispatchResultWithPostInfo;21502151 2152 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21532154 2155 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2156}215721582159pub trait RefungibleExtensions<T>2160where2161 T: Config,2162{2163 2164 2165 2166 2167 2168 2169 2170 fn repartition(2171 &self,2172 sender: &T::CrossAccountId,2173 token: TokenId,2174 amount: u128,2175 ) -> DispatchResultWithPostInfo;2176}21772178217921802181pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2182 let post_info = PostDispatchInfo {2183 actual_weight: Some(weight),2184 pays_fee: Pays::Yes,2185 };2186 match res {2187 Ok(()) => Ok(post_info),2188 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2189 }2190}21912192impl<T: Config> From<PropertiesError> for Error<T> {2193 fn from(error: PropertiesError) -> Self {2194 match error {2195 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2196 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2197 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2198 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2199 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2200 }2201 }2202}