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 if admin {1587 let amount = amount1588 .checked_add(1)1589 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1590 ensure!(1591 amount <= Self::collection_admins_limit(),1592 <Error<T>>::CollectionAdminCountExceeded,1593 );15941595 <AdminAmount<T>>::insert(collection.id, amount);1596 <IsAdmin<T>>::insert((collection.id, user), true);15971598 Self::deposit_event(Event::<T>::CollectionAdminAdded(1599 collection.id,1600 user.clone(),1601 ));1602 } else {1603 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1604 <IsAdmin<T>>::remove((collection.id, user));16051606 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1607 collection.id,1608 user.clone(),1609 ));1610 }16111612 <PalletEvm<T>>::deposit_log(1613 erc::CollectionHelpersEvents::CollectionChanged {1614 collection_id: eth::collection_id_to_address(collection.id),1615 }1616 .to_log(T::ContractAddress::get()),1617 );16181619 Ok(())1620 }16211622 1623 pub fn update_limits(1624 user: &T::CrossAccountId,1625 collection: &mut CollectionHandle<T>,1626 new_limit: CollectionLimits,1627 ) -> DispatchResult {1628 collection.check_is_internal()?;1629 collection.check_is_owner_or_admin(user)?;16301631 collection.limits =1632 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16331634 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1635 <PalletEvm<T>>::deposit_log(1636 erc::CollectionHelpersEvents::CollectionChanged {1637 collection_id: eth::collection_id_to_address(collection.id),1638 }1639 .to_log(T::ContractAddress::get()),1640 );16411642 collection.save()1643 }16441645 1646 fn clamp_limits(1647 mode: CollectionMode,1648 old_limit: &CollectionLimits,1649 mut new_limit: CollectionLimits,1650 ) -> Result<CollectionLimits, DispatchError> {1651 let limits = old_limit;1652 limit_default!(old_limit, new_limit,1653 account_token_ownership_limit => ensure!(1654 new_limit <= MAX_TOKEN_OWNERSHIP,1655 <Error<T>>::CollectionLimitBoundsExceeded,1656 ),1657 sponsored_data_size => ensure!(1658 new_limit <= CUSTOM_DATA_LIMIT,1659 <Error<T>>::CollectionLimitBoundsExceeded,1660 ),16611662 sponsored_data_rate_limit => {},1663 token_limit => ensure!(1664 old_limit >= new_limit && new_limit > 0,1665 <Error<T>>::CollectionTokenLimitExceeded1666 ),16671668 sponsor_transfer_timeout(match mode {1669 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1670 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1671 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1672 }) => ensure!(1673 new_limit <= MAX_SPONSOR_TIMEOUT,1674 <Error<T>>::CollectionLimitBoundsExceeded,1675 ),1676 sponsor_approve_timeout => {},1677 owner_can_transfer => ensure!(1678 !limits.owner_can_transfer_instaled() ||1679 old_limit || !new_limit,1680 <Error<T>>::OwnerPermissionsCantBeReverted,1681 ),1682 owner_can_destroy => ensure!(1683 old_limit || !new_limit,1684 <Error<T>>::OwnerPermissionsCantBeReverted,1685 ),1686 transfers_enabled => {},1687 );1688 Ok(new_limit)1689 }16901691 1692 pub fn update_permissions(1693 user: &T::CrossAccountId,1694 collection: &mut CollectionHandle<T>,1695 new_permission: CollectionPermissions,1696 ) -> DispatchResult {1697 collection.check_is_internal()?;1698 collection.check_is_owner_or_admin(user)?;1699 collection.permissions = Self::clamp_permissions(1700 collection.mode.clone(),1701 &collection.permissions,1702 new_permission,1703 )?;17041705 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1706 <PalletEvm<T>>::deposit_log(1707 erc::CollectionHelpersEvents::CollectionChanged {1708 collection_id: eth::collection_id_to_address(collection.id),1709 }1710 .to_log(T::ContractAddress::get()),1711 );17121713 collection.save()1714 }17151716 1717 fn clamp_permissions(1718 _mode: CollectionMode,1719 old_permission: &CollectionPermissions,1720 mut new_permission: CollectionPermissions,1721 ) -> Result<CollectionPermissions, DispatchError> {1722 limit_default_clone!(old_permission, new_permission,1723 access => {},1724 mint_mode => {},1725 nesting => { },1726 );1727 Ok(new_permission)1728 }1729}173017311732#[macro_export]1733macro_rules! unsupported {1734 ($runtime:path) => {1735 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1736 };1737}173817391740pub trait CommonWeightInfo<CrossAccountId> {1741 1742 fn create_item() -> Weight;17431744 1745 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17461747 1748 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17491750 1751 fn burn_item() -> Weight;17521753 1754 1755 1756 fn set_collection_properties(amount: u32) -> Weight;17571758 1759 1760 1761 fn delete_collection_properties(amount: u32) -> Weight;17621763 1764 1765 1766 fn set_token_properties(amount: u32) -> Weight;17671768 1769 1770 1771 fn delete_token_properties(amount: u32) -> Weight;17721773 1774 1775 1776 fn set_token_property_permissions(amount: u32) -> Weight;17771778 1779 fn transfer() -> Weight;17801781 1782 fn approve() -> Weight;17831784 1785 fn transfer_from() -> Weight;17861787 1788 fn burn_from() -> Weight;17891790 1791 1792 1793 1794 fn burn_recursively_self_raw() -> Weight;17951796 1797 1798 1799 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18001801 1802 1803 1804 1805 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1806 Self::burn_recursively_self_raw()1807 .saturating_mul(max_selfs.max(1) as u64)1808 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1809 }18101811 1812 fn token_owner() -> Weight;18131814 1815 fn set_allowance_for_all() -> Weight;1816}181718181819pub trait RefungibleExtensionsWeightInfo {1820 1821 fn repartition() -> Weight;1822}182318241825182618271828pub trait CommonCollectionOperations<T: Config> {1829 1830 1831 1832 1833 1834 1835 fn create_item(1836 &self,1837 sender: T::CrossAccountId,1838 to: T::CrossAccountId,1839 data: CreateItemData,1840 nesting_budget: &dyn Budget,1841 ) -> DispatchResultWithPostInfo;18421843 1844 1845 1846 1847 1848 1849 fn create_multiple_items(1850 &self,1851 sender: T::CrossAccountId,1852 to: T::CrossAccountId,1853 data: Vec<CreateItemData>,1854 nesting_budget: &dyn Budget,1855 ) -> DispatchResultWithPostInfo;18561857 1858 1859 1860 1861 1862 1863 fn create_multiple_items_ex(1864 &self,1865 sender: T::CrossAccountId,1866 data: CreateItemExData<T::CrossAccountId>,1867 nesting_budget: &dyn Budget,1868 ) -> DispatchResultWithPostInfo;18691870 1871 1872 1873 1874 1875 fn burn_item(1876 &self,1877 sender: T::CrossAccountId,1878 token: TokenId,1879 amount: u128,1880 ) -> DispatchResultWithPostInfo;18811882 1883 1884 1885 1886 1887 1888 fn burn_item_recursively(1889 &self,1890 sender: T::CrossAccountId,1891 token: TokenId,1892 self_budget: &dyn Budget,1893 breadth_budget: &dyn Budget,1894 ) -> DispatchResultWithPostInfo;18951896 1897 1898 1899 1900 fn set_collection_properties(1901 &self,1902 sender: T::CrossAccountId,1903 properties: Vec<Property>,1904 ) -> DispatchResultWithPostInfo;19051906 1907 1908 1909 1910 fn delete_collection_properties(1911 &self,1912 sender: &T::CrossAccountId,1913 property_keys: Vec<PropertyKey>,1914 ) -> DispatchResultWithPostInfo;19151916 1917 1918 1919 1920 1921 1922 1923 1924 1925 fn set_token_properties(1926 &self,1927 sender: T::CrossAccountId,1928 token_id: TokenId,1929 properties: Vec<Property>,1930 budget: &dyn Budget,1931 ) -> DispatchResultWithPostInfo;19321933 1934 1935 1936 1937 1938 1939 1940 1941 1942 fn delete_token_properties(1943 &self,1944 sender: T::CrossAccountId,1945 token_id: TokenId,1946 property_keys: Vec<PropertyKey>,1947 budget: &dyn Budget,1948 ) -> DispatchResultWithPostInfo;19491950 1951 1952 1953 1954 1955 1956 fn set_token_property_permissions(1957 &self,1958 sender: &T::CrossAccountId,1959 property_permissions: Vec<PropertyKeyPermission>,1960 ) -> DispatchResultWithPostInfo;19611962 1963 1964 1965 1966 1967 1968 1969 fn transfer(1970 &self,1971 sender: T::CrossAccountId,1972 to: T::CrossAccountId,1973 token: TokenId,1974 amount: u128,1975 budget: &dyn Budget,1976 ) -> DispatchResultWithPostInfo;19771978 1979 1980 1981 1982 1983 1984 fn approve(1985 &self,1986 sender: T::CrossAccountId,1987 spender: T::CrossAccountId,1988 token: TokenId,1989 amount: u128,1990 ) -> DispatchResultWithPostInfo;19911992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 fn transfer_from(2003 &self,2004 sender: T::CrossAccountId,2005 from: T::CrossAccountId,2006 to: T::CrossAccountId,2007 token: TokenId,2008 amount: u128,2009 budget: &dyn Budget,2010 ) -> DispatchResultWithPostInfo;20112012 2013 2014 2015 2016 2017 2018 2019 2020 2021 fn burn_from(2022 &self,2023 sender: T::CrossAccountId,2024 from: T::CrossAccountId,2025 token: TokenId,2026 amount: u128,2027 budget: &dyn Budget,2028 ) -> DispatchResultWithPostInfo;20292030 2031 2032 2033 2034 2035 2036 fn check_nesting(2037 &self,2038 sender: T::CrossAccountId,2039 from: (CollectionId, TokenId),2040 under: TokenId,2041 budget: &dyn Budget,2042 ) -> DispatchResult;20432044 2045 2046 2047 2048 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20492050 2051 2052 2053 2054 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20552056 2057 2058 2059 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;20602061 2062 fn collection_tokens(&self) -> Vec<TokenId>;20632064 2065 2066 2067 fn token_exists(&self, token: TokenId) -> bool;20682069 2070 fn last_token_id(&self) -> TokenId;20712072 2073 2074 2075 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;20762077 2078 2079 2080 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;20812082 2083 2084 2085 2086 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;20872088 2089 2090 2091 2092 2093 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;20942095 2096 fn total_supply(&self) -> u32;20972098 2099 2100 2101 fn account_balance(&self, account: T::CrossAccountId) -> u32;21022103 2104 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21052106 2107 fn total_pieces(&self, token: TokenId) -> Option<u128>;21082109 2110 2111 2112 2113 2114 fn allowance(2115 &self,2116 sender: T::CrossAccountId,2117 spender: T::CrossAccountId,2118 token: TokenId,2119 ) -> u128;21202121 2122 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21232124 2125 2126 2127 2128 fn set_allowance_for_all(2129 &self,2130 owner: T::CrossAccountId,2131 operator: T::CrossAccountId,2132 approve: bool,2133 ) -> DispatchResultWithPostInfo;21342135 2136 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;2137}213821392140pub trait RefungibleExtensions<T>2141where2142 T: Config,2143{2144 2145 2146 2147 2148 2149 2150 2151 fn repartition(2152 &self,2153 sender: &T::CrossAccountId,2154 token: TokenId,2155 amount: u128,2156 ) -> DispatchResultWithPostInfo;2157}21582159216021612162pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2163 let post_info = PostDispatchInfo {2164 actual_weight: Some(weight),2165 pays_fee: Pays::Yes,2166 };2167 match res {2168 Ok(()) => Ok(post_info),2169 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2170 }2171}21722173impl<T: Config> From<PropertiesError> for Error<T> {2174 fn from(error: PropertiesError) -> Self {2175 match error {2176 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2177 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2178 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2179 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2180 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2181 }2182 }2183}