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 pub fn set_scoped_collection_property(1231 collection_id: CollectionId,1232 scope: PropertyScope,1233 property: Property,1234 ) -> DispatchResult {1235 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1236 properties.try_scoped_set(scope, property.key, property.value)1237 })1238 .map_err(<Error<T>>::from)?;12391240 Ok(())1241 }12421243 1244 1245 1246 1247 1248 pub fn set_scoped_collection_properties(1249 collection_id: CollectionId,1250 scope: PropertyScope,1251 properties: impl Iterator<Item = Property>,1252 ) -> DispatchResult {1253 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1254 stored_properties.try_scoped_set_from_iter(scope, properties)1255 })1256 .map_err(<Error<T>>::from)?;12571258 Ok(())1259 }12601261 1262 1263 1264 1265 1266 #[transactional]1267 pub fn set_collection_properties(1268 collection: &CollectionHandle<T>,1269 sender: &T::CrossAccountId,1270 properties: Vec<Property>,1271 ) -> DispatchResult {1272 for property in properties {1273 Self::set_collection_property(collection, sender, property)?;1274 }12751276 Ok(())1277 }12781279 1280 1281 1282 1283 1284 pub fn delete_collection_property(1285 collection: &CollectionHandle<T>,1286 sender: &T::CrossAccountId,1287 property_key: PropertyKey,1288 ) -> DispatchResult {1289 collection.check_is_owner_or_admin(sender)?;12901291 CollectionProperties::<T>::try_mutate(collection.id, |properties| {1292 properties.remove(&property_key)1293 })1294 .map_err(<Error<T>>::from)?;12951296 Self::deposit_event(Event::CollectionPropertyDeleted(1297 collection.id,1298 property_key,1299 ));1300 <PalletEvm<T>>::deposit_log(1301 erc::CollectionHelpersEvents::CollectionChanged {1302 collection_id: eth::collection_id_to_address(collection.id),1303 }1304 .to_log(T::ContractAddress::get()),1305 );13061307 Ok(())1308 }13091310 1311 1312 1313 1314 1315 #[transactional]1316 pub fn delete_collection_properties(1317 collection: &CollectionHandle<T>,1318 sender: &T::CrossAccountId,1319 property_keys: Vec<PropertyKey>,1320 ) -> DispatchResult {1321 for key in property_keys {1322 Self::delete_collection_property(collection, sender, key)?;1323 }13241325 Ok(())1326 }13271328 1329 1330 1331 1332 1333 1334 pub fn set_property_permission_unchecked(1335 collection: CollectionId,1336 property_permission: PropertyKeyPermission,1337 ) -> DispatchResult {1338 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1339 permissions.try_set(property_permission.key, property_permission.permission)1340 })1341 .map_err(<Error<T>>::from)?;1342 Ok(())1343 }13441345 1346 1347 1348 1349 1350 pub fn set_property_permission(1351 collection: &CollectionHandle<T>,1352 sender: &T::CrossAccountId,1353 property_permission: PropertyKeyPermission,1354 ) -> DispatchResult {1355 Self::set_scoped_property_permission(1356 collection,1357 sender,1358 PropertyScope::None,1359 property_permission,1360 )1361 }13621363 1364 1365 1366 1367 1368 1369 pub fn set_scoped_property_permission(1370 collection: &CollectionHandle<T>,1371 sender: &T::CrossAccountId,1372 scope: PropertyScope,1373 property_permission: PropertyKeyPermission,1374 ) -> DispatchResult {1375 collection.check_is_owner_or_admin(sender)?;13761377 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1378 let current_permission = all_permissions.get(&property_permission.key);1379 if matches![1380 current_permission,1381 Some(PropertyPermission { mutable: false, .. })1382 ] {1383 return Err(<Error<T>>::NoPermission.into());1384 }13851386 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1387 let property_permission = property_permission.clone();1388 permissions.try_scoped_set(1389 scope,1390 property_permission.key,1391 property_permission.permission,1392 )1393 })1394 .map_err(<Error<T>>::from)?;13951396 Self::deposit_event(Event::PropertyPermissionSet(1397 collection.id,1398 property_permission.key,1399 ));1400 <PalletEvm<T>>::deposit_log(1401 erc::CollectionHelpersEvents::CollectionChanged {1402 collection_id: eth::collection_id_to_address(collection.id),1403 }1404 .to_log(T::ContractAddress::get()),1405 );14061407 Ok(())1408 }14091410 1411 1412 1413 1414 1415 #[transactional]1416 pub fn set_token_property_permissions(1417 collection: &CollectionHandle<T>,1418 sender: &T::CrossAccountId,1419 property_permissions: Vec<PropertyKeyPermission>,1420 ) -> DispatchResult {1421 Self::set_scoped_token_property_permissions(1422 collection,1423 sender,1424 PropertyScope::None,1425 property_permissions,1426 )1427 }14281429 1430 1431 1432 1433 1434 1435 #[transactional]1436 pub fn set_scoped_token_property_permissions(1437 collection: &CollectionHandle<T>,1438 sender: &T::CrossAccountId,1439 scope: PropertyScope,1440 property_permissions: Vec<PropertyKeyPermission>,1441 ) -> DispatchResult {1442 for prop_pemission in property_permissions {1443 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1444 }14451446 Ok(())1447 }14481449 1450 pub fn get_collection_property(1451 collection_id: CollectionId,1452 key: &PropertyKey,1453 ) -> Option<PropertyValue> {1454 Self::collection_properties(collection_id).get(key).cloned()1455 }14561457 1458 pub fn bytes_keys_to_property_keys(1459 keys: Vec<Vec<u8>>,1460 ) -> Result<Vec<PropertyKey>, DispatchError> {1461 keys.into_iter()1462 .map(|key| -> Result<PropertyKey, DispatchError> {1463 key.try_into()1464 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1465 })1466 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1467 }14681469 1470 pub fn filter_collection_properties(1471 collection_id: CollectionId,1472 keys: Option<Vec<PropertyKey>>,1473 ) -> Result<Vec<Property>, DispatchError> {1474 let properties = Self::collection_properties(collection_id);14751476 let properties = keys1477 .map(|keys| {1478 keys.into_iter()1479 .filter_map(|key| {1480 properties.get(&key).map(|value| Property {1481 key,1482 value: value.clone(),1483 })1484 })1485 .collect()1486 })1487 .unwrap_or_else(|| {1488 properties1489 .into_iter()1490 .map(|(key, value)| Property { key, value })1491 .collect()1492 });14931494 Ok(properties)1495 }14961497 1498 pub fn filter_property_permissions(1499 collection_id: CollectionId,1500 keys: Option<Vec<PropertyKey>>,1501 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1502 let permissions = Self::property_permissions(collection_id);15031504 let key_permissions = keys1505 .map(|keys| {1506 keys.into_iter()1507 .filter_map(|key| {1508 permissions1509 .get(&key)1510 .map(|permission| PropertyKeyPermission {1511 key,1512 permission: permission.clone(),1513 })1514 })1515 .collect()1516 })1517 .unwrap_or_else(|| {1518 permissions1519 .into_iter()1520 .map(|(key, permission)| PropertyKeyPermission { key, permission })1521 .collect()1522 });15231524 Ok(key_permissions)1525 }15261527 1528 1529 1530 pub fn toggle_allowlist(1531 collection: &CollectionHandle<T>,1532 sender: &T::CrossAccountId,1533 user: &T::CrossAccountId,1534 allowed: bool,1535 ) -> DispatchResult {1536 collection.check_is_owner_or_admin(sender)?;15371538 15391540 if allowed {1541 <Allowlist<T>>::insert((collection.id, user), true);1542 Self::deposit_event(Event::<T>::AllowListAddressAdded(1543 collection.id,1544 user.clone(),1545 ));1546 } else {1547 <Allowlist<T>>::remove((collection.id, user));1548 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1549 collection.id,1550 user.clone(),1551 ));1552 }15531554 <PalletEvm<T>>::deposit_log(1555 erc::CollectionHelpersEvents::CollectionChanged {1556 collection_id: eth::collection_id_to_address(collection.id),1557 }1558 .to_log(T::ContractAddress::get()),1559 );15601561 Ok(())1562 }15631564 1565 1566 1567 pub fn toggle_admin(1568 collection: &CollectionHandle<T>,1569 sender: &T::CrossAccountId,1570 user: &T::CrossAccountId,1571 admin: bool,1572 ) -> DispatchResult {1573 collection.check_is_internal()?;1574 collection.check_is_owner(sender)?;15751576 let is_admin = <IsAdmin<T>>::get((collection.id, user));1577 if is_admin == admin {1578 if admin {1579 return Ok(());1580 } else {1581 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1582 }1583 }1584 let amount = <AdminAmount<T>>::get(collection.id);15851586 15871588 if admin {1589 let amount = amount1590 .checked_add(1)1591 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1592 ensure!(1593 amount <= Self::collection_admins_limit(),1594 <Error<T>>::CollectionAdminCountExceeded,1595 );15961597 <AdminAmount<T>>::insert(collection.id, amount);1598 <IsAdmin<T>>::insert((collection.id, user), true);15991600 Self::deposit_event(Event::<T>::CollectionAdminAdded(1601 collection.id,1602 user.clone(),1603 ));1604 } else {1605 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1606 <IsAdmin<T>>::remove((collection.id, user));16071608 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1609 collection.id,1610 user.clone(),1611 ));1612 }16131614 <PalletEvm<T>>::deposit_log(1615 erc::CollectionHelpersEvents::CollectionChanged {1616 collection_id: eth::collection_id_to_address(collection.id),1617 }1618 .to_log(T::ContractAddress::get()),1619 );16201621 Ok(())1622 }16231624 1625 pub fn update_limits(1626 user: &T::CrossAccountId,1627 collection: &mut CollectionHandle<T>,1628 new_limit: CollectionLimits,1629 ) -> DispatchResult {1630 collection.check_is_internal()?;1631 collection.check_is_owner_or_admin(user)?;16321633 collection.limits =1634 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16351636 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1637 <PalletEvm<T>>::deposit_log(1638 erc::CollectionHelpersEvents::CollectionChanged {1639 collection_id: eth::collection_id_to_address(collection.id),1640 }1641 .to_log(T::ContractAddress::get()),1642 );16431644 collection.save()1645 }16461647 1648 fn clamp_limits(1649 mode: CollectionMode,1650 old_limit: &CollectionLimits,1651 mut new_limit: CollectionLimits,1652 ) -> Result<CollectionLimits, DispatchError> {1653 let limits = old_limit;1654 limit_default!(old_limit, new_limit,1655 account_token_ownership_limit => ensure!(1656 new_limit <= MAX_TOKEN_OWNERSHIP,1657 <Error<T>>::CollectionLimitBoundsExceeded,1658 ),1659 sponsored_data_size => ensure!(1660 new_limit <= CUSTOM_DATA_LIMIT,1661 <Error<T>>::CollectionLimitBoundsExceeded,1662 ),16631664 sponsored_data_rate_limit => {},1665 token_limit => ensure!(1666 old_limit >= new_limit && new_limit > 0,1667 <Error<T>>::CollectionTokenLimitExceeded1668 ),16691670 sponsor_transfer_timeout(match mode {1671 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1672 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1673 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1674 }) => ensure!(1675 new_limit <= MAX_SPONSOR_TIMEOUT,1676 <Error<T>>::CollectionLimitBoundsExceeded,1677 ),1678 sponsor_approve_timeout => {},1679 owner_can_transfer => ensure!(1680 !limits.owner_can_transfer_instaled() ||1681 old_limit || !new_limit,1682 <Error<T>>::OwnerPermissionsCantBeReverted,1683 ),1684 owner_can_destroy => ensure!(1685 old_limit || !new_limit,1686 <Error<T>>::OwnerPermissionsCantBeReverted,1687 ),1688 transfers_enabled => {},1689 );1690 Ok(new_limit)1691 }16921693 1694 pub fn update_permissions(1695 user: &T::CrossAccountId,1696 collection: &mut CollectionHandle<T>,1697 new_permission: CollectionPermissions,1698 ) -> DispatchResult {1699 collection.check_is_internal()?;1700 collection.check_is_owner_or_admin(user)?;1701 collection.permissions = Self::clamp_permissions(1702 collection.mode.clone(),1703 &collection.permissions,1704 new_permission,1705 )?;17061707 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1708 <PalletEvm<T>>::deposit_log(1709 erc::CollectionHelpersEvents::CollectionChanged {1710 collection_id: eth::collection_id_to_address(collection.id),1711 }1712 .to_log(T::ContractAddress::get()),1713 );17141715 collection.save()1716 }17171718 1719 fn clamp_permissions(1720 _mode: CollectionMode,1721 old_permission: &CollectionPermissions,1722 mut new_permission: CollectionPermissions,1723 ) -> Result<CollectionPermissions, DispatchError> {1724 limit_default_clone!(old_permission, new_permission,1725 access => {},1726 mint_mode => {},1727 nesting => { },1728 );1729 Ok(new_permission)1730 }1731}173217331734#[macro_export]1735macro_rules! unsupported {1736 ($runtime:path) => {1737 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1738 };1739}174017411742pub trait CommonWeightInfo<CrossAccountId> {1743 1744 fn create_item() -> Weight;17451746 1747 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17481749 1750 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17511752 1753 fn burn_item() -> Weight;17541755 1756 1757 1758 fn set_collection_properties(amount: u32) -> Weight;17591760 1761 1762 1763 fn delete_collection_properties(amount: u32) -> Weight;17641765 1766 1767 1768 fn set_token_properties(amount: u32) -> Weight;17691770 1771 1772 1773 fn delete_token_properties(amount: u32) -> Weight;17741775 1776 1777 1778 fn set_token_property_permissions(amount: u32) -> Weight;17791780 1781 fn transfer() -> Weight;17821783 1784 fn approve() -> Weight;17851786 1787 fn transfer_from() -> Weight;17881789 1790 fn burn_from() -> Weight;17911792 1793 1794 1795 1796 fn burn_recursively_self_raw() -> Weight;17971798 1799 1800 1801 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18021803 1804 1805 1806 1807 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1808 Self::burn_recursively_self_raw()1809 .saturating_mul(max_selfs.max(1) as u64)1810 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1811 }18121813 1814 fn token_owner() -> Weight;18151816 1817 fn set_allowance_for_all() -> Weight;18181819 1820 fn repair_item() -> Weight;1821}182218231824pub trait RefungibleExtensionsWeightInfo {1825 1826 fn repartition() -> Weight;1827}182818291830183118321833pub trait CommonCollectionOperations<T: Config> {1834 1835 1836 1837 1838 1839 1840 fn create_item(1841 &self,1842 sender: T::CrossAccountId,1843 to: T::CrossAccountId,1844 data: CreateItemData,1845 nesting_budget: &dyn Budget,1846 ) -> DispatchResultWithPostInfo;18471848 1849 1850 1851 1852 1853 1854 fn create_multiple_items(1855 &self,1856 sender: T::CrossAccountId,1857 to: T::CrossAccountId,1858 data: Vec<CreateItemData>,1859 nesting_budget: &dyn Budget,1860 ) -> DispatchResultWithPostInfo;18611862 1863 1864 1865 1866 1867 1868 fn create_multiple_items_ex(1869 &self,1870 sender: T::CrossAccountId,1871 data: CreateItemExData<T::CrossAccountId>,1872 nesting_budget: &dyn Budget,1873 ) -> DispatchResultWithPostInfo;18741875 1876 1877 1878 1879 1880 fn burn_item(1881 &self,1882 sender: T::CrossAccountId,1883 token: TokenId,1884 amount: u128,1885 ) -> DispatchResultWithPostInfo;18861887 1888 1889 1890 1891 1892 1893 fn burn_item_recursively(1894 &self,1895 sender: T::CrossAccountId,1896 token: TokenId,1897 self_budget: &dyn Budget,1898 breadth_budget: &dyn Budget,1899 ) -> DispatchResultWithPostInfo;19001901 1902 1903 1904 1905 fn set_collection_properties(1906 &self,1907 sender: T::CrossAccountId,1908 properties: Vec<Property>,1909 ) -> DispatchResultWithPostInfo;19101911 1912 1913 1914 1915 fn delete_collection_properties(1916 &self,1917 sender: &T::CrossAccountId,1918 property_keys: Vec<PropertyKey>,1919 ) -> DispatchResultWithPostInfo;19201921 1922 1923 1924 1925 1926 1927 1928 1929 1930 fn set_token_properties(1931 &self,1932 sender: T::CrossAccountId,1933 token_id: TokenId,1934 properties: Vec<Property>,1935 budget: &dyn Budget,1936 ) -> DispatchResultWithPostInfo;19371938 1939 1940 1941 1942 1943 1944 1945 1946 1947 fn delete_token_properties(1948 &self,1949 sender: T::CrossAccountId,1950 token_id: TokenId,1951 property_keys: Vec<PropertyKey>,1952 budget: &dyn Budget,1953 ) -> DispatchResultWithPostInfo;19541955 1956 1957 1958 1959 1960 1961 fn set_token_property_permissions(1962 &self,1963 sender: &T::CrossAccountId,1964 property_permissions: Vec<PropertyKeyPermission>,1965 ) -> DispatchResultWithPostInfo;19661967 1968 1969 1970 1971 1972 1973 1974 fn transfer(1975 &self,1976 sender: T::CrossAccountId,1977 to: T::CrossAccountId,1978 token: TokenId,1979 amount: u128,1980 budget: &dyn Budget,1981 ) -> DispatchResultWithPostInfo;19821983 1984 1985 1986 1987 1988 1989 fn approve(1990 &self,1991 sender: T::CrossAccountId,1992 spender: T::CrossAccountId,1993 token: TokenId,1994 amount: u128,1995 ) -> DispatchResultWithPostInfo;19961997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 fn transfer_from(2008 &self,2009 sender: T::CrossAccountId,2010 from: T::CrossAccountId,2011 to: T::CrossAccountId,2012 token: TokenId,2013 amount: u128,2014 budget: &dyn Budget,2015 ) -> DispatchResultWithPostInfo;20162017 2018 2019 2020 2021 2022 2023 2024 2025 2026 fn burn_from(2027 &self,2028 sender: T::CrossAccountId,2029 from: T::CrossAccountId,2030 token: TokenId,2031 amount: u128,2032 budget: &dyn Budget,2033 ) -> DispatchResultWithPostInfo;20342035 2036 2037 2038 2039 2040 2041 fn check_nesting(2042 &self,2043 sender: T::CrossAccountId,2044 from: (CollectionId, TokenId),2045 under: TokenId,2046 budget: &dyn Budget,2047 ) -> DispatchResult;20482049 2050 2051 2052 2053 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20542055 2056 2057 2058 2059 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20602061 2062 2063 2064 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;20652066 2067 fn collection_tokens(&self) -> Vec<TokenId>;20682069 2070 2071 2072 fn token_exists(&self, token: TokenId) -> bool;20732074 2075 fn last_token_id(&self) -> TokenId;20762077 2078 2079 2080 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;20812082 2083 2084 2085 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;20862087 2088 2089 2090 2091 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;20922093 2094 2095 2096 2097 2098 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;20992100 2101 fn total_supply(&self) -> u32;21022103 2104 2105 2106 fn account_balance(&self, account: T::CrossAccountId) -> u32;21072108 2109 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21102111 2112 fn total_pieces(&self, token: TokenId) -> Option<u128>;21132114 2115 2116 2117 2118 2119 fn allowance(2120 &self,2121 sender: T::CrossAccountId,2122 spender: T::CrossAccountId,2123 token: TokenId,2124 ) -> u128;21252126 2127 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21282129 2130 2131 2132 2133 fn set_allowance_for_all(2134 &self,2135 owner: T::CrossAccountId,2136 operator: T::CrossAccountId,2137 approve: bool,2138 ) -> DispatchResultWithPostInfo;21392140 2141 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21422143 2144 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2145}214621472148pub trait RefungibleExtensions<T>2149where2150 T: Config,2151{2152 2153 2154 2155 2156 2157 2158 2159 fn repartition(2160 &self,2161 sender: &T::CrossAccountId,2162 token: TokenId,2163 amount: u128,2164 ) -> DispatchResultWithPostInfo;2165}21662167216821692170pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2171 let post_info = PostDispatchInfo {2172 actual_weight: Some(weight),2173 pays_fee: Pays::Yes,2174 };2175 match res {2176 Ok(()) => Ok(post_info),2177 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2178 }2179}21802181impl<T: Config> From<PropertiesError> for Error<T> {2182 fn from(error: PropertiesError) -> Self {2183 match error {2184 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2185 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2186 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2187 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2188 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2189 }2190 }2191}