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;1818}181918201821pub trait RefungibleExtensionsWeightInfo {1822 1823 fn repartition() -> Weight;1824}182518261827182818291830pub trait CommonCollectionOperations<T: Config> {1831 1832 1833 1834 1835 1836 1837 fn create_item(1838 &self,1839 sender: T::CrossAccountId,1840 to: T::CrossAccountId,1841 data: CreateItemData,1842 nesting_budget: &dyn Budget,1843 ) -> DispatchResultWithPostInfo;18441845 1846 1847 1848 1849 1850 1851 fn create_multiple_items(1852 &self,1853 sender: T::CrossAccountId,1854 to: T::CrossAccountId,1855 data: Vec<CreateItemData>,1856 nesting_budget: &dyn Budget,1857 ) -> DispatchResultWithPostInfo;18581859 1860 1861 1862 1863 1864 1865 fn create_multiple_items_ex(1866 &self,1867 sender: T::CrossAccountId,1868 data: CreateItemExData<T::CrossAccountId>,1869 nesting_budget: &dyn Budget,1870 ) -> DispatchResultWithPostInfo;18711872 1873 1874 1875 1876 1877 fn burn_item(1878 &self,1879 sender: T::CrossAccountId,1880 token: TokenId,1881 amount: u128,1882 ) -> DispatchResultWithPostInfo;18831884 1885 1886 1887 1888 1889 1890 fn burn_item_recursively(1891 &self,1892 sender: T::CrossAccountId,1893 token: TokenId,1894 self_budget: &dyn Budget,1895 breadth_budget: &dyn Budget,1896 ) -> DispatchResultWithPostInfo;18971898 1899 1900 1901 1902 fn set_collection_properties(1903 &self,1904 sender: T::CrossAccountId,1905 properties: Vec<Property>,1906 ) -> DispatchResultWithPostInfo;19071908 1909 1910 1911 1912 fn delete_collection_properties(1913 &self,1914 sender: &T::CrossAccountId,1915 property_keys: Vec<PropertyKey>,1916 ) -> DispatchResultWithPostInfo;19171918 1919 1920 1921 1922 1923 1924 1925 1926 1927 fn set_token_properties(1928 &self,1929 sender: T::CrossAccountId,1930 token_id: TokenId,1931 properties: Vec<Property>,1932 budget: &dyn Budget,1933 ) -> DispatchResultWithPostInfo;19341935 1936 1937 1938 1939 1940 1941 1942 1943 1944 fn delete_token_properties(1945 &self,1946 sender: T::CrossAccountId,1947 token_id: TokenId,1948 property_keys: Vec<PropertyKey>,1949 budget: &dyn Budget,1950 ) -> DispatchResultWithPostInfo;19511952 1953 1954 1955 1956 1957 1958 fn set_token_property_permissions(1959 &self,1960 sender: &T::CrossAccountId,1961 property_permissions: Vec<PropertyKeyPermission>,1962 ) -> DispatchResultWithPostInfo;19631964 1965 1966 1967 1968 1969 1970 1971 fn transfer(1972 &self,1973 sender: T::CrossAccountId,1974 to: T::CrossAccountId,1975 token: TokenId,1976 amount: u128,1977 budget: &dyn Budget,1978 ) -> DispatchResultWithPostInfo;19791980 1981 1982 1983 1984 1985 1986 fn approve(1987 &self,1988 sender: T::CrossAccountId,1989 spender: T::CrossAccountId,1990 token: TokenId,1991 amount: u128,1992 ) -> DispatchResultWithPostInfo;19931994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 fn transfer_from(2005 &self,2006 sender: T::CrossAccountId,2007 from: T::CrossAccountId,2008 to: T::CrossAccountId,2009 token: TokenId,2010 amount: u128,2011 budget: &dyn Budget,2012 ) -> DispatchResultWithPostInfo;20132014 2015 2016 2017 2018 2019 2020 2021 2022 2023 fn burn_from(2024 &self,2025 sender: T::CrossAccountId,2026 from: T::CrossAccountId,2027 token: TokenId,2028 amount: u128,2029 budget: &dyn Budget,2030 ) -> DispatchResultWithPostInfo;20312032 2033 2034 2035 2036 2037 2038 fn check_nesting(2039 &self,2040 sender: T::CrossAccountId,2041 from: (CollectionId, TokenId),2042 under: TokenId,2043 budget: &dyn Budget,2044 ) -> DispatchResult;20452046 2047 2048 2049 2050 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20512052 2053 2054 2055 2056 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20572058 2059 2060 2061 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;20622063 2064 fn collection_tokens(&self) -> Vec<TokenId>;20652066 2067 2068 2069 fn token_exists(&self, token: TokenId) -> bool;20702071 2072 fn last_token_id(&self) -> TokenId;20732074 2075 2076 2077 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;20782079 2080 2081 2082 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;20832084 2085 2086 2087 2088 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;20892090 2091 2092 2093 2094 2095 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;20962097 2098 fn total_supply(&self) -> u32;20992100 2101 2102 2103 fn account_balance(&self, account: T::CrossAccountId) -> u32;21042105 2106 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21072108 2109 fn total_pieces(&self, token: TokenId) -> Option<u128>;21102111 2112 2113 2114 2115 2116 fn allowance(2117 &self,2118 sender: T::CrossAccountId,2119 spender: T::CrossAccountId,2120 token: TokenId,2121 ) -> u128;21222123 2124 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21252126 2127 2128 2129 2130 fn set_allowance_for_all(2131 &self,2132 owner: T::CrossAccountId,2133 operator: T::CrossAccountId,2134 approve: bool,2135 ) -> DispatchResultWithPostInfo;21362137 2138 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;2139}214021412142pub trait RefungibleExtensions<T>2143where2144 T: Config,2145{2146 2147 2148 2149 2150 2151 2152 2153 fn repartition(2154 &self,2155 sender: &T::CrossAccountId,2156 token: TokenId,2157 amount: u128,2158 ) -> DispatchResultWithPostInfo;2159}21602161216221632164pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2165 let post_info = PostDispatchInfo {2166 actual_weight: Some(weight),2167 pays_fee: Pays::Yes,2168 };2169 match res {2170 Ok(()) => Ok(post_info),2171 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2172 }2173}21742175impl<T: Config> From<PropertiesError> for Error<T> {2176 fn from(error: PropertiesError) -> Self {2177 match error {2178 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2179 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2180 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2181 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2182 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2183 }2184 }2185}