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};118use up_pov_estimate_rpc::PovInfo;119120pub use pallet::*;121use sp_core::H160;122use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};123#[cfg(feature = "runtime-benchmarks")]124pub mod benchmarking;125pub mod dispatch;126pub mod erc;127pub mod eth;128pub mod weights;129130131pub type SelfWeightOf<T> = <T as Config>::WeightInfo;132133134135136137138139#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]140pub struct CollectionHandle<T: Config> {141 142 pub id: CollectionId,143 collection: Collection<T::AccountId>,144 145 pub recorder: SubstrateRecorder<T>,146}147148impl<T: Config> WithRecorder<T> for CollectionHandle<T> {149 fn recorder(&self) -> &SubstrateRecorder<T> {150 &self.recorder151 }152 fn into_recorder(self) -> SubstrateRecorder<T> {153 self.recorder154 }155}156157impl<T: Config> CollectionHandle<T> {158 159 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {160 <CollectionById<T>>::get(id).map(|collection| Self {161 id,162 collection,163 recorder: SubstrateRecorder::new(gas_limit),164 })165 }166167 168 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {169 <CollectionById<T>>::get(id).map(|collection| Self {170 id,171 collection,172 recorder,173 })174 }175176 177 178 pub fn new(id: CollectionId) -> Option<Self> {179 Self::new_with_gas_limit(id, u64::MAX)180 }181182 183 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {184 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)185 }186187 188 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {189 self.recorder190 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(191 <T as frame_system::Config>::DbWeight::get()192 .read193 .saturating_mul(reads),194 )))195 }196197 198 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {199 self.recorder200 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(201 <T as frame_system::Config>::DbWeight::get()202 .write203 .saturating_mul(writes),204 )))205 }206207 208 pub fn consume_store_reads_and_writes(209 &self,210 reads: u64,211 writes: u64,212 ) -> evm_coder::execution::Result<()> {213 let weight = <T as frame_system::Config>::DbWeight::get();214 let reads = weight.read.saturating_mul(reads);215 let writes = weight.read.saturating_mul(writes);216 self.recorder217 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(218 reads.saturating_add(writes),219 )))220 }221222 223 pub fn save(&self) -> DispatchResult {224 <CollectionById<T>>::insert(self.id, &self.collection);225 Ok(())226 }227228 229 230 231 232 233 pub fn set_sponsor(234 &mut self,235 sender: &T::CrossAccountId,236 sponsor: T::AccountId,237 ) -> DispatchResult {238 self.check_is_internal()?;239 self.check_is_owner_or_admin(sender)?;240241 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());242243 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));244 <PalletEvm<T>>::deposit_log(245 erc::CollectionHelpersEvents::CollectionChanged {246 collection_id: eth::collection_id_to_address(self.id),247 }248 .to_log(T::ContractAddress::get()),249 );250251 self.save()252 }253254 255 256 257 258 259 260 261 262 263 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {264 self.check_is_internal()?;265266 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());267268 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));269 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));270 <PalletEvm<T>>::deposit_log(271 erc::CollectionHelpersEvents::CollectionChanged {272 collection_id: eth::collection_id_to_address(self.id),273 }274 .to_log(T::ContractAddress::get()),275 );276277 self.save()278 }279280 281 282 283 284 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {285 self.check_is_internal()?;286 ensure!(287 self.collection.sponsorship.pending_sponsor() == Some(sender),288 Error::<T>::ConfirmSponsorshipFail289 );290291 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());292293 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));294 <PalletEvm<T>>::deposit_log(295 erc::CollectionHelpersEvents::CollectionChanged {296 collection_id: eth::collection_id_to_address(self.id),297 }298 .to_log(T::ContractAddress::get()),299 );300301 self.save()302 }303304 305 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {306 self.check_is_internal()?;307 self.check_is_owner_or_admin(sender)?;308309 self.collection.sponsorship = SponsorshipState::Disabled;310311 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));312 <PalletEvm<T>>::deposit_log(313 erc::CollectionHelpersEvents::CollectionChanged {314 collection_id: eth::collection_id_to_address(self.id),315 }316 .to_log(T::ContractAddress::get()),317 );318 self.save()319 }320321 322 323 324 325 pub fn force_remove_sponsor(&mut self) -> DispatchResult {326 self.check_is_internal()?;327328 self.collection.sponsorship = SponsorshipState::Disabled;329330 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));331 <PalletEvm<T>>::deposit_log(332 erc::CollectionHelpersEvents::CollectionChanged {333 collection_id: eth::collection_id_to_address(self.id),334 }335 .to_log(T::ContractAddress::get()),336 );337 self.save()338 }339340 341 342 pub fn check_is_internal(&self) -> DispatchResult {343 if self.flags.external {344 return Err(<Error<T>>::CollectionIsExternal)?;345 }346347 Ok(())348 }349350 351 352 pub fn check_is_external(&self) -> DispatchResult {353 if !self.flags.external {354 return Err(<Error<T>>::CollectionIsInternal)?;355 }356357 Ok(())358 }359}360361impl<T: Config> Deref for CollectionHandle<T> {362 type Target = Collection<T::AccountId>;363364 fn deref(&self) -> &Self::Target {365 &self.collection366 }367}368369impl<T: Config> DerefMut for CollectionHandle<T> {370 fn deref_mut(&mut self) -> &mut Self::Target {371 &mut self.collection372 }373}374375impl<T: Config> CollectionHandle<T> {376 377 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {378 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);379 Ok(())380 }381382 383 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {384 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))385 }386387 388 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {389 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);390 Ok(())391 }392393 394 395 396 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {397 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)398 }399400 401 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {402 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)403 }404405 406 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {407 ensure!(408 <Allowlist<T>>::get((self.id, user)),409 <Error<T>>::AddressNotInAllowlist410 );411 Ok(())412 }413414 415 416 417 pub fn change_owner(418 &mut self,419 caller: T::CrossAccountId,420 new_owner: T::CrossAccountId,421 ) -> DispatchResult {422 self.check_is_internal()?;423 self.check_is_owner(&caller)?;424 self.collection.owner = new_owner.as_sub().clone();425426 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(427 self.id,428 new_owner.as_sub().clone(),429 ));430 <PalletEvm<T>>::deposit_log(431 erc::CollectionHelpersEvents::CollectionChanged {432 collection_id: eth::collection_id_to_address(self.id),433 }434 .to_log(T::ContractAddress::get()),435 );436437 self.save()438 }439}440441#[frame_support::pallet]442pub mod pallet {443 use super::*;444 use dispatch::CollectionDispatch;445 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};446 use frame_system::pallet_prelude::*;447 use frame_support::traits::Currency;448 use up_data_structs::{TokenId, mapping::TokenAddressMapping};449 use scale_info::TypeInfo;450 use weights::WeightInfo;451452 #[pallet::config]453 pub trait Config:454 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo455 {456 457 type WeightInfo: WeightInfo;458459 460 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;461462 463 type Currency: Currency<Self::AccountId>;464465 466 #[pallet::constant]467 type CollectionCreationPrice: Get<468 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,469 >;470471 472 type CollectionDispatch: CollectionDispatch<Self>;473474 475 type TreasuryAccountId: Get<Self::AccountId>;476477 478 #[pallet::constant]479 type ContractAddress: Get<H160>;480481 482 type EvmTokenAddressMapping: TokenAddressMapping<H160>;483484 485 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;486 }487488 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);489490 #[pallet::pallet]491 #[pallet::storage_version(STORAGE_VERSION)]492 #[pallet::generate_store(pub(super) trait Store)]493 pub struct Pallet<T>(_);494495 #[pallet::extra_constants]496 impl<T: Config> Pallet<T> {497 498 pub fn collection_admins_limit() -> u32 {499 COLLECTION_ADMINS_LIMIT500 }501 }502503 #[pallet::event]504 #[pallet::generate_deposit(pub fn deposit_event)]505 pub enum Event<T: Config> {506 507 CollectionCreated(508 509 CollectionId,510 511 u8,512 513 T::AccountId,514 ),515516 517 CollectionDestroyed(518 519 CollectionId,520 ),521522 523 ItemCreated(524 525 CollectionId,526 527 TokenId,528 529 T::CrossAccountId,530 531 u128,532 ),533534 535 ItemDestroyed(536 537 CollectionId,538 539 TokenId,540 541 T::CrossAccountId,542 543 u128,544 ),545546 547 Transfer(548 549 CollectionId,550 551 TokenId,552 553 T::CrossAccountId,554 555 T::CrossAccountId,556 557 u128,558 ),559560 561 Approved(562 563 CollectionId,564 565 TokenId,566 567 T::CrossAccountId,568 569 T::CrossAccountId,570 571 u128,572 ),573574 575 ApprovedForAll(576 577 CollectionId,578 579 T::CrossAccountId,580 581 T::CrossAccountId,582 583 bool,584 ),585586 587 CollectionPropertySet(588 589 CollectionId,590 591 PropertyKey,592 ),593594 595 CollectionPropertyDeleted(596 597 CollectionId,598 599 PropertyKey,600 ),601602 603 TokenPropertySet(604 605 CollectionId,606 607 TokenId,608 609 PropertyKey,610 ),611612 613 TokenPropertyDeleted(614 615 CollectionId,616 617 TokenId,618 619 PropertyKey,620 ),621622 623 PropertyPermissionSet(624 625 CollectionId,626 627 PropertyKey,628 ),629630 631 AllowListAddressAdded(632 633 CollectionId,634 635 T::CrossAccountId,636 ),637638 639 AllowListAddressRemoved(640 641 CollectionId,642 643 T::CrossAccountId,644 ),645646 647 CollectionAdminAdded(648 649 CollectionId,650 651 T::CrossAccountId,652 ),653654 655 CollectionAdminRemoved(656 657 CollectionId,658 659 T::CrossAccountId,660 ),661662 663 CollectionLimitSet(664 665 CollectionId,666 ),667668 669 CollectionOwnerChanged(670 671 CollectionId,672 673 T::AccountId,674 ),675676 677 CollectionPermissionSet(678 679 CollectionId,680 ),681682 683 CollectionSponsorSet(684 685 CollectionId,686 687 T::AccountId,688 ),689690 691 SponsorshipConfirmed(692 693 CollectionId,694 695 T::AccountId,696 ),697698 699 CollectionSponsorRemoved(700 701 CollectionId,702 ),703 }704705 #[pallet::error]706 pub enum Error<T> {707 708 CollectionNotFound,709 710 MustBeTokenOwner,711 712 NoPermission,713 714 CantDestroyNotEmptyCollection,715 716 PublicMintingNotAllowed,717 718 AddressNotInAllowlist,719720 721 CollectionNameLimitExceeded,722 723 CollectionDescriptionLimitExceeded,724 725 CollectionTokenPrefixLimitExceeded,726 727 TotalCollectionsLimitExceeded,728 729 CollectionAdminCountExceeded,730 731 CollectionLimitBoundsExceeded,732 733 OwnerPermissionsCantBeReverted,734 735 TransferNotAllowed,736 737 AccountTokenLimitExceeded,738 739 CollectionTokenLimitExceeded,740 741 MetadataFlagFrozen,742743 744 TokenNotFound,745 746 TokenValueTooLow,747 748 ApprovedValueTooLow,749 750 CantApproveMoreThanOwned,751 752 AddressIsNotEthMirror,753754 755 AddressIsZero,756757 758 UnsupportedOperation,759760 761 NotSufficientFounds,762763 764 UserIsNotAllowedToNest,765 766 SourceCollectionIsNotAllowedToNest,767768 769 CollectionFieldSizeExceeded,770771 772 NoSpaceForProperty,773774 775 PropertyLimitReached,776777 778 PropertyKeyIsTooLong,779780 781 InvalidCharacterInPropertyKey,782783 784 EmptyPropertyKey,785786 787 CollectionIsExternal,788789 790 CollectionIsInternal,791792 793 ConfirmSponsorshipFail,794795 796 UserIsNotCollectionAdmin,797 }798799 800 #[pallet::storage]801 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;802803 804 #[pallet::storage]805 pub type DestroyedCollectionCount<T> =806 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;807808 809 #[pallet::storage]810 pub type CollectionById<T> = StorageMap<811 Hasher = Blake2_128Concat,812 Key = CollectionId,813 Value = Collection<<T as frame_system::Config>::AccountId>,814 QueryKind = OptionQuery,815 >;816817 818 #[pallet::storage]819 #[pallet::getter(fn collection_properties)]820 pub type CollectionProperties<T> = StorageMap<821 Hasher = Blake2_128Concat,822 Key = CollectionId,823 Value = Properties,824 QueryKind = ValueQuery,825 OnEmpty = up_data_structs::CollectionProperties,826 >;827828 829 #[pallet::storage]830 #[pallet::getter(fn property_permissions)]831 pub type CollectionPropertyPermissions<T> = StorageMap<832 Hasher = Blake2_128Concat,833 Key = CollectionId,834 Value = PropertiesPermissionMap,835 QueryKind = ValueQuery,836 >;837838 839 #[pallet::storage]840 pub type AdminAmount<T> = StorageMap<841 Hasher = Blake2_128Concat,842 Key = CollectionId,843 Value = u32,844 QueryKind = ValueQuery,845 >;846847 848 #[pallet::storage]849 pub type IsAdmin<T: Config> = StorageNMap<850 Key = (851 Key<Blake2_128Concat, CollectionId>,852 Key<Blake2_128Concat, T::CrossAccountId>,853 ),854 Value = bool,855 QueryKind = ValueQuery,856 >;857858 859 #[pallet::storage]860 pub type Allowlist<T: Config> = StorageNMap<861 Key = (862 Key<Blake2_128Concat, CollectionId>,863 Key<Blake2_128Concat, T::CrossAccountId>,864 ),865 Value = bool,866 QueryKind = ValueQuery,867 >;868869 870 #[pallet::storage]871 pub type DummyStorageValue<T: Config> = StorageValue<872 Value = (873 CollectionStats,874 CollectionId,875 TokenId,876 TokenChild,877 PhantomType<(878 TokenData<T::CrossAccountId>,879 RpcCollection<T::AccountId>,880 881 RmrkCollectionInfo<T::AccountId>,882 RmrkInstanceInfo<T::AccountId>,883 RmrkResourceInfo,884 RmrkPropertyInfo,885 RmrkBaseInfo<T::AccountId>,886 RmrkPartType,887 RmrkBoundedTheme,888 RmrkNftChild,889 890 PovInfo,891 )>,892 ),893 QueryKind = OptionQuery,894 >;895896 #[pallet::hooks]897 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {898 fn on_runtime_upgrade() -> Weight {899 StorageVersion::new(1).put::<Pallet<T>>();900901 Weight::zero()902 }903 }904}905906impl<T: Config> Pallet<T> {907 908 909 910 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {911 ensure!(912 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,913 <Error<T>>::AddressIsZero914 );915 Ok(())916 }917918 919 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {920 <IsAdmin<T>>::iter_prefix((collection,))921 .map(|(a, _)| a)922 .collect()923 }924925 926 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {927 <Allowlist<T>>::iter_prefix((collection,))928 .map(|(a, _)| a)929 .collect()930 }931932 933 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {934 <Allowlist<T>>::get((collection, user))935 }936937 938 pub fn collection_stats() -> CollectionStats {939 let created = <CreatedCollectionCount<T>>::get();940 let destroyed = <DestroyedCollectionCount<T>>::get();941 CollectionStats {942 created: created.0,943 destroyed: destroyed.0,944 alive: created.0 - destroyed.0,945 }946 }947948 949 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {950 let collection = <CollectionById<T>>::get(collection)?;951 let limits = collection.limits;952 let effective_limits = CollectionLimits {953 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),954 sponsored_data_size: Some(limits.sponsored_data_size()),955 sponsored_data_rate_limit: Some(956 limits957 .sponsored_data_rate_limit958 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),959 ),960 token_limit: Some(limits.token_limit()),961 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(962 match collection.mode {963 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,964 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,965 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,966 },967 )),968 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),969 owner_can_transfer: Some(limits.owner_can_transfer()),970 owner_can_destroy: Some(limits.owner_can_destroy()),971 transfers_enabled: Some(limits.transfers_enabled()),972 };973974 Some(effective_limits)975 }976977 978 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {979 let Collection {980 name,981 description,982 owner,983 mode,984 token_prefix,985 sponsorship,986 limits,987 permissions,988 flags,989 } = <CollectionById<T>>::get(collection)?;990991 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)992 .into_iter()993 .map(|(key, permission)| PropertyKeyPermission { key, permission })994 .collect();995996 let properties = <CollectionProperties<T>>::get(collection)997 .into_iter()998 .map(|(key, value)| Property { key, value })999 .collect();10001001 let permissions = CollectionPermissions {1002 access: Some(permissions.access()),1003 mint_mode: Some(permissions.mint_mode()),1004 nesting: Some(permissions.nesting().clone()),1005 };10061007 Some(RpcCollection {1008 name: name.into_inner(),1009 description: description.into_inner(),1010 owner,1011 mode,1012 token_prefix: token_prefix.into_inner(),1013 sponsorship,1014 limits,1015 permissions,1016 token_property_permissions,1017 properties,1018 read_only: flags.external,10191020 flags: RpcCollectionFlags {1021 foreign: flags.foreign,1022 erc721metadata: flags.erc721metadata,1023 },1024 })1025 }1026}10271028macro_rules! limit_default {1029 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1030 $(1031 if let Some($new) = $new.$field {1032 let $old = $old.$field($($arg)?);1033 let _ = $new;1034 let _ = $old;1035 $check1036 } else {1037 $new.$field = $old.$field1038 }1039 )*1040 }};1041}1042macro_rules! limit_default_clone {1043 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1044 $(1045 if let Some($new) = $new.$field.clone() {1046 let $old = $old.$field($($arg)?);1047 let _ = $new;1048 let _ = $old;1049 $check1050 } else {1051 $new.$field = $old.$field.clone()1052 }1053 )*1054 }};1055}10561057impl<T: Config> Pallet<T> {1058 1059 1060 1061 1062 1063 pub fn init_collection(1064 owner: T::CrossAccountId,1065 payer: T::CrossAccountId,1066 data: CreateCollectionData<T::AccountId>,1067 flags: CollectionFlags,1068 ) -> Result<CollectionId, DispatchError> {1069 {1070 ensure!(1071 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1072 Error::<T>::CollectionTokenPrefixLimitExceeded1073 );1074 }10751076 let created_count = <CreatedCollectionCount<T>>::get()1077 .01078 .checked_add(1)1079 .ok_or(ArithmeticError::Overflow)?;1080 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1081 let id = CollectionId(created_count);10821083 1084 ensure!(1085 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1086 <Error<T>>::TotalCollectionsLimitExceeded1087 );10881089 10901091 let collection = Collection {1092 owner: owner.as_sub().clone(),1093 name: data.name,1094 mode: data.mode.clone(),1095 description: data.description,1096 token_prefix: data.token_prefix,1097 sponsorship: data1098 .pending_sponsor1099 .map(SponsorshipState::Unconfirmed)1100 .unwrap_or_default(),1101 limits: data1102 .limits1103 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1104 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1105 permissions: data1106 .permissions1107 .map(|permissions| {1108 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1109 })1110 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1111 flags,1112 };11131114 let mut collection_properties = up_data_structs::CollectionProperties::get();1115 collection_properties1116 .try_set_from_iter(data.properties.into_iter())1117 .map_err(<Error<T>>::from)?;11181119 CollectionProperties::<T>::insert(id, collection_properties);11201121 let mut token_props_permissions = PropertiesPermissionMap::new();1122 token_props_permissions1123 .try_set_from_iter(data.token_property_permissions.into_iter())1124 .map_err(<Error<T>>::from)?;11251126 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11271128 1129 {1130 let mut imbalance =1131 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1132 imbalance.subsume(1133 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1134 &T::TreasuryAccountId::get(),1135 T::CollectionCreationPrice::get(),1136 ),1137 );1138 <T as Config>::Currency::settle(1139 payer.as_sub(),1140 imbalance,1141 WithdrawReasons::TRANSFER,1142 ExistenceRequirement::KeepAlive,1143 )1144 .map_err(|_| Error::<T>::NotSufficientFounds)?;1145 }11461147 <CreatedCollectionCount<T>>::put(created_count);1148 <Pallet<T>>::deposit_event(Event::CollectionCreated(1149 id,1150 data.mode.id(),1151 owner.as_sub().clone(),1152 ));1153 <PalletEvm<T>>::deposit_log(1154 erc::CollectionHelpersEvents::CollectionCreated {1155 owner: *owner.as_eth(),1156 collection_id: eth::collection_id_to_address(id),1157 }1158 .to_log(T::ContractAddress::get()),1159 );1160 <CollectionById<T>>::insert(id, collection);1161 Ok(id)1162 }11631164 1165 1166 1167 1168 pub fn destroy_collection(1169 collection: CollectionHandle<T>,1170 sender: &T::CrossAccountId,1171 ) -> DispatchResult {1172 ensure!(1173 collection.limits.owner_can_destroy(),1174 <Error<T>>::NoPermission,1175 );1176 collection.check_is_owner(sender)?;11771178 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1179 .01180 .checked_add(1)1181 .ok_or(ArithmeticError::Overflow)?;11821183 11841185 <DestroyedCollectionCount<T>>::put(destroyed_collections);1186 <CollectionById<T>>::remove(collection.id);1187 <AdminAmount<T>>::remove(collection.id);1188 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1189 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1190 <CollectionProperties<T>>::remove(collection.id);11911192 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11931194 <PalletEvm<T>>::deposit_log(1195 erc::CollectionHelpersEvents::CollectionDestroyed {1196 collection_id: eth::collection_id_to_address(collection.id),1197 }1198 .to_log(T::ContractAddress::get()),1199 );1200 Ok(())1201 }12021203 1204 1205 1206 1207 1208 1209 1210 1211 #[transactional]1212 fn modify_collection_properties(1213 collection: &CollectionHandle<T>,1214 sender: &T::CrossAccountId,1215 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1216 ) -> DispatchResult {1217 collection.check_is_owner_or_admin(sender)?;12181219 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12201221 for (key, value) in properties_updates {1222 match value {1223 Some(value) => {1224 stored_properties1225 .try_set(key.clone(), value)1226 .map_err(<Error<T>>::from)?;12271228 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1229 <PalletEvm<T>>::deposit_log(1230 erc::CollectionHelpersEvents::CollectionChanged {1231 collection_id: eth::collection_id_to_address(collection.id),1232 }1233 .to_log(T::ContractAddress::get()),1234 );1235 }1236 None => {1237 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12381239 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1240 <PalletEvm<T>>::deposit_log(1241 erc::CollectionHelpersEvents::CollectionChanged {1242 collection_id: eth::collection_id_to_address(collection.id),1243 }1244 .to_log(T::ContractAddress::get()),1245 );1246 }1247 }1248 }12491250 <CollectionProperties<T>>::set(collection.id, stored_properties);12511252 Ok(())1253 }12541255 1256 1257 1258 1259 1260 pub fn set_collection_property(1261 collection: &CollectionHandle<T>,1262 sender: &T::CrossAccountId,1263 property: Property,1264 ) -> DispatchResult {1265 Self::set_collection_properties(collection, sender, [property].into_iter())1266 }12671268 1269 1270 1271 1272 1273 1274 pub fn set_scoped_collection_property(1275 collection_id: CollectionId,1276 scope: PropertyScope,1277 property: Property,1278 ) -> DispatchResult {1279 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1280 properties.try_scoped_set(scope, property.key, property.value)1281 })1282 .map_err(<Error<T>>::from)?;12831284 Ok(())1285 }12861287 1288 1289 1290 1291 1292 1293 pub fn set_scoped_collection_properties(1294 collection_id: CollectionId,1295 scope: PropertyScope,1296 properties: impl Iterator<Item = Property>,1297 ) -> DispatchResult {1298 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1299 stored_properties.try_scoped_set_from_iter(scope, properties)1300 })1301 .map_err(<Error<T>>::from)?;13021303 Ok(())1304 }13051306 1307 1308 1309 1310 1311 pub fn set_collection_properties(1312 collection: &CollectionHandle<T>,1313 sender: &T::CrossAccountId,1314 properties: impl Iterator<Item = Property>,1315 ) -> DispatchResult {1316 Self::modify_collection_properties(1317 collection,1318 sender,1319 properties.map(|property| (property.key, Some(property.value))),1320 )1321 }13221323 1324 1325 1326 1327 1328 pub fn delete_collection_property(1329 collection: &CollectionHandle<T>,1330 sender: &T::CrossAccountId,1331 property_key: PropertyKey,1332 ) -> DispatchResult {1333 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1334 }13351336 1337 1338 1339 1340 1341 pub fn delete_collection_properties(1342 collection: &CollectionHandle<T>,1343 sender: &T::CrossAccountId,1344 property_keys: impl Iterator<Item = PropertyKey>,1345 ) -> DispatchResult {1346 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1347 }13481349 1350 1351 1352 1353 1354 1355 pub fn set_property_permission_unchecked(1356 collection: CollectionId,1357 property_permission: PropertyKeyPermission,1358 ) -> DispatchResult {1359 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1360 permissions.try_set(property_permission.key, property_permission.permission)1361 })1362 .map_err(<Error<T>>::from)?;1363 Ok(())1364 }13651366 1367 1368 1369 1370 1371 pub fn set_property_permission(1372 collection: &CollectionHandle<T>,1373 sender: &T::CrossAccountId,1374 property_permission: PropertyKeyPermission,1375 ) -> DispatchResult {1376 Self::set_scoped_property_permission(1377 collection,1378 sender,1379 PropertyScope::None,1380 property_permission,1381 )1382 }13831384 1385 1386 1387 1388 1389 1390 pub fn set_scoped_property_permission(1391 collection: &CollectionHandle<T>,1392 sender: &T::CrossAccountId,1393 scope: PropertyScope,1394 property_permission: PropertyKeyPermission,1395 ) -> DispatchResult {1396 collection.check_is_owner_or_admin(sender)?;13971398 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1399 let current_permission = all_permissions.get(&property_permission.key);1400 if matches![1401 current_permission,1402 Some(PropertyPermission { mutable: false, .. })1403 ] {1404 return Err(<Error<T>>::NoPermission.into());1405 }14061407 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1408 let property_permission = property_permission.clone();1409 permissions.try_scoped_set(1410 scope,1411 property_permission.key,1412 property_permission.permission,1413 )1414 })1415 .map_err(<Error<T>>::from)?;14161417 Self::deposit_event(Event::PropertyPermissionSet(1418 collection.id,1419 property_permission.key,1420 ));1421 <PalletEvm<T>>::deposit_log(1422 erc::CollectionHelpersEvents::CollectionChanged {1423 collection_id: eth::collection_id_to_address(collection.id),1424 }1425 .to_log(T::ContractAddress::get()),1426 );14271428 Ok(())1429 }14301431 1432 1433 1434 1435 1436 #[transactional]1437 pub fn set_token_property_permissions(1438 collection: &CollectionHandle<T>,1439 sender: &T::CrossAccountId,1440 property_permissions: Vec<PropertyKeyPermission>,1441 ) -> DispatchResult {1442 Self::set_scoped_token_property_permissions(1443 collection,1444 sender,1445 PropertyScope::None,1446 property_permissions,1447 )1448 }14491450 1451 1452 1453 1454 1455 1456 #[transactional]1457 pub fn set_scoped_token_property_permissions(1458 collection: &CollectionHandle<T>,1459 sender: &T::CrossAccountId,1460 scope: PropertyScope,1461 property_permissions: Vec<PropertyKeyPermission>,1462 ) -> DispatchResult {1463 for prop_pemission in property_permissions {1464 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1465 }14661467 Ok(())1468 }14691470 1471 pub fn get_collection_property(1472 collection_id: CollectionId,1473 key: &PropertyKey,1474 ) -> Option<PropertyValue> {1475 Self::collection_properties(collection_id).get(key).cloned()1476 }14771478 1479 pub fn bytes_keys_to_property_keys(1480 keys: Vec<Vec<u8>>,1481 ) -> Result<Vec<PropertyKey>, DispatchError> {1482 keys.into_iter()1483 .map(|key| -> Result<PropertyKey, DispatchError> {1484 key.try_into()1485 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1486 })1487 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1488 }14891490 1491 pub fn filter_collection_properties(1492 collection_id: CollectionId,1493 keys: Option<Vec<PropertyKey>>,1494 ) -> Result<Vec<Property>, DispatchError> {1495 let properties = Self::collection_properties(collection_id);14961497 let properties = keys1498 .map(|keys| {1499 keys.into_iter()1500 .filter_map(|key| {1501 properties.get(&key).map(|value| Property {1502 key,1503 value: value.clone(),1504 })1505 })1506 .collect()1507 })1508 .unwrap_or_else(|| {1509 properties1510 .into_iter()1511 .map(|(key, value)| Property { key, value })1512 .collect()1513 });15141515 Ok(properties)1516 }15171518 1519 pub fn filter_property_permissions(1520 collection_id: CollectionId,1521 keys: Option<Vec<PropertyKey>>,1522 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1523 let permissions = Self::property_permissions(collection_id);15241525 let key_permissions = keys1526 .map(|keys| {1527 keys.into_iter()1528 .filter_map(|key| {1529 permissions1530 .get(&key)1531 .map(|permission| PropertyKeyPermission {1532 key,1533 permission: permission.clone(),1534 })1535 })1536 .collect()1537 })1538 .unwrap_or_else(|| {1539 permissions1540 .into_iter()1541 .map(|(key, permission)| PropertyKeyPermission { key, permission })1542 .collect()1543 });15441545 Ok(key_permissions)1546 }15471548 1549 1550 1551 pub fn toggle_allowlist(1552 collection: &CollectionHandle<T>,1553 sender: &T::CrossAccountId,1554 user: &T::CrossAccountId,1555 allowed: bool,1556 ) -> DispatchResult {1557 collection.check_is_owner_or_admin(sender)?;15581559 15601561 if allowed {1562 <Allowlist<T>>::insert((collection.id, user), true);1563 Self::deposit_event(Event::<T>::AllowListAddressAdded(1564 collection.id,1565 user.clone(),1566 ));1567 } else {1568 <Allowlist<T>>::remove((collection.id, user));1569 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1570 collection.id,1571 user.clone(),1572 ));1573 }15741575 <PalletEvm<T>>::deposit_log(1576 erc::CollectionHelpersEvents::CollectionChanged {1577 collection_id: eth::collection_id_to_address(collection.id),1578 }1579 .to_log(T::ContractAddress::get()),1580 );15811582 Ok(())1583 }15841585 1586 1587 1588 pub fn toggle_admin(1589 collection: &CollectionHandle<T>,1590 sender: &T::CrossAccountId,1591 user: &T::CrossAccountId,1592 admin: bool,1593 ) -> DispatchResult {1594 collection.check_is_internal()?;1595 collection.check_is_owner(sender)?;15961597 let is_admin = <IsAdmin<T>>::get((collection.id, user));1598 if is_admin == admin {1599 if admin {1600 return Ok(());1601 } else {1602 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1603 }1604 }1605 let amount = <AdminAmount<T>>::get(collection.id);16061607 16081609 if admin {1610 let amount = amount1611 .checked_add(1)1612 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1613 ensure!(1614 amount <= Self::collection_admins_limit(),1615 <Error<T>>::CollectionAdminCountExceeded,1616 );16171618 <AdminAmount<T>>::insert(collection.id, amount);1619 <IsAdmin<T>>::insert((collection.id, user), true);16201621 Self::deposit_event(Event::<T>::CollectionAdminAdded(1622 collection.id,1623 user.clone(),1624 ));1625 } else {1626 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1627 <IsAdmin<T>>::remove((collection.id, user));16281629 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1630 collection.id,1631 user.clone(),1632 ));1633 }16341635 <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 Ok(())1643 }16441645 1646 pub fn update_limits(1647 user: &T::CrossAccountId,1648 collection: &mut CollectionHandle<T>,1649 new_limit: CollectionLimits,1650 ) -> DispatchResult {1651 collection.check_is_internal()?;1652 collection.check_is_owner_or_admin(user)?;16531654 collection.limits =1655 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16561657 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1658 <PalletEvm<T>>::deposit_log(1659 erc::CollectionHelpersEvents::CollectionChanged {1660 collection_id: eth::collection_id_to_address(collection.id),1661 }1662 .to_log(T::ContractAddress::get()),1663 );16641665 collection.save()1666 }16671668 1669 fn clamp_limits(1670 mode: CollectionMode,1671 old_limit: &CollectionLimits,1672 mut new_limit: CollectionLimits,1673 ) -> Result<CollectionLimits, DispatchError> {1674 let limits = old_limit;1675 limit_default!(old_limit, new_limit,1676 account_token_ownership_limit => ensure!(1677 new_limit <= MAX_TOKEN_OWNERSHIP,1678 <Error<T>>::CollectionLimitBoundsExceeded,1679 ),1680 sponsored_data_size => ensure!(1681 new_limit <= CUSTOM_DATA_LIMIT,1682 <Error<T>>::CollectionLimitBoundsExceeded,1683 ),16841685 sponsored_data_rate_limit => {},1686 token_limit => ensure!(1687 old_limit >= new_limit && new_limit > 0,1688 <Error<T>>::CollectionTokenLimitExceeded1689 ),16901691 sponsor_transfer_timeout(match mode {1692 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1693 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1694 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1695 }) => ensure!(1696 new_limit <= MAX_SPONSOR_TIMEOUT,1697 <Error<T>>::CollectionLimitBoundsExceeded,1698 ),1699 sponsor_approve_timeout => {},1700 owner_can_transfer => ensure!(1701 !limits.owner_can_transfer_instaled() ||1702 old_limit || !new_limit,1703 <Error<T>>::OwnerPermissionsCantBeReverted,1704 ),1705 owner_can_destroy => ensure!(1706 old_limit || !new_limit,1707 <Error<T>>::OwnerPermissionsCantBeReverted,1708 ),1709 transfers_enabled => {},1710 );1711 Ok(new_limit)1712 }17131714 1715 pub fn update_permissions(1716 user: &T::CrossAccountId,1717 collection: &mut CollectionHandle<T>,1718 new_permission: CollectionPermissions,1719 ) -> DispatchResult {1720 collection.check_is_internal()?;1721 collection.check_is_owner_or_admin(user)?;1722 collection.permissions = Self::clamp_permissions(1723 collection.mode.clone(),1724 &collection.permissions,1725 new_permission,1726 )?;17271728 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1729 <PalletEvm<T>>::deposit_log(1730 erc::CollectionHelpersEvents::CollectionChanged {1731 collection_id: eth::collection_id_to_address(collection.id),1732 }1733 .to_log(T::ContractAddress::get()),1734 );17351736 collection.save()1737 }17381739 1740 fn clamp_permissions(1741 _mode: CollectionMode,1742 old_permission: &CollectionPermissions,1743 mut new_permission: CollectionPermissions,1744 ) -> Result<CollectionPermissions, DispatchError> {1745 limit_default_clone!(old_permission, new_permission,1746 access => {},1747 mint_mode => {},1748 nesting => { },1749 );1750 Ok(new_permission)1751 }17521753 1754 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1755 CollectionProperties::<T>::mutate(collection_id, |properties| {1756 properties.recompute_consumed_space();1757 });17581759 Ok(())1760 }1761}176217631764#[macro_export]1765macro_rules! unsupported {1766 ($runtime:path) => {1767 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1768 };1769}177017711772pub trait CommonWeightInfo<CrossAccountId> {1773 1774 fn create_item() -> Weight;17751776 1777 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17781779 1780 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17811782 1783 fn burn_item() -> Weight;17841785 1786 1787 1788 fn set_collection_properties(amount: u32) -> Weight;17891790 1791 1792 1793 fn delete_collection_properties(amount: u32) -> Weight;17941795 1796 1797 1798 fn set_token_properties(amount: u32) -> Weight;17991800 1801 1802 1803 fn delete_token_properties(amount: u32) -> Weight;18041805 1806 1807 1808 fn set_token_property_permissions(amount: u32) -> Weight;18091810 1811 fn transfer() -> Weight;18121813 1814 fn approve() -> Weight;18151816 1817 fn approve_from() -> Weight;18181819 1820 fn transfer_from() -> Weight;18211822 1823 fn burn_from() -> Weight;18241825 1826 1827 1828 1829 fn burn_recursively_self_raw() -> Weight;18301831 1832 1833 1834 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18351836 1837 1838 1839 1840 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1841 Self::burn_recursively_self_raw()1842 .saturating_mul(max_selfs.max(1) as u64)1843 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1844 }18451846 1847 fn token_owner() -> Weight;18481849 1850 fn set_allowance_for_all() -> Weight;18511852 1853 fn force_repair_item() -> Weight;1854}185518561857pub trait RefungibleExtensionsWeightInfo {1858 1859 fn repartition() -> Weight;1860}186118621863186418651866pub trait CommonCollectionOperations<T: Config> {1867 1868 1869 1870 1871 1872 1873 fn create_item(1874 &self,1875 sender: T::CrossAccountId,1876 to: T::CrossAccountId,1877 data: CreateItemData,1878 nesting_budget: &dyn Budget,1879 ) -> DispatchResultWithPostInfo;18801881 1882 1883 1884 1885 1886 1887 fn create_multiple_items(1888 &self,1889 sender: T::CrossAccountId,1890 to: T::CrossAccountId,1891 data: Vec<CreateItemData>,1892 nesting_budget: &dyn Budget,1893 ) -> DispatchResultWithPostInfo;18941895 1896 1897 1898 1899 1900 1901 fn create_multiple_items_ex(1902 &self,1903 sender: T::CrossAccountId,1904 data: CreateItemExData<T::CrossAccountId>,1905 nesting_budget: &dyn Budget,1906 ) -> DispatchResultWithPostInfo;19071908 1909 1910 1911 1912 1913 fn burn_item(1914 &self,1915 sender: T::CrossAccountId,1916 token: TokenId,1917 amount: u128,1918 ) -> DispatchResultWithPostInfo;19191920 1921 1922 1923 1924 1925 1926 fn burn_item_recursively(1927 &self,1928 sender: T::CrossAccountId,1929 token: TokenId,1930 self_budget: &dyn Budget,1931 breadth_budget: &dyn Budget,1932 ) -> DispatchResultWithPostInfo;19331934 1935 1936 1937 1938 fn set_collection_properties(1939 &self,1940 sender: T::CrossAccountId,1941 properties: Vec<Property>,1942 ) -> DispatchResultWithPostInfo;19431944 1945 1946 1947 1948 fn delete_collection_properties(1949 &self,1950 sender: &T::CrossAccountId,1951 property_keys: Vec<PropertyKey>,1952 ) -> DispatchResultWithPostInfo;19531954 1955 1956 1957 1958 1959 1960 1961 1962 1963 fn set_token_properties(1964 &self,1965 sender: T::CrossAccountId,1966 token_id: TokenId,1967 properties: Vec<Property>,1968 budget: &dyn Budget,1969 ) -> DispatchResultWithPostInfo;19701971 1972 1973 1974 1975 1976 1977 1978 1979 1980 fn delete_token_properties(1981 &self,1982 sender: T::CrossAccountId,1983 token_id: TokenId,1984 property_keys: Vec<PropertyKey>,1985 budget: &dyn Budget,1986 ) -> DispatchResultWithPostInfo;19871988 1989 1990 1991 1992 1993 1994 fn set_token_property_permissions(1995 &self,1996 sender: &T::CrossAccountId,1997 property_permissions: Vec<PropertyKeyPermission>,1998 ) -> DispatchResultWithPostInfo;19992000 2001 2002 2003 2004 2005 2006 2007 fn transfer(2008 &self,2009 sender: T::CrossAccountId,2010 to: T::CrossAccountId,2011 token: TokenId,2012 amount: u128,2013 budget: &dyn Budget,2014 ) -> DispatchResultWithPostInfo;20152016 2017 2018 2019 2020 2021 2022 fn approve(2023 &self,2024 sender: T::CrossAccountId,2025 spender: T::CrossAccountId,2026 token: TokenId,2027 amount: u128,2028 ) -> DispatchResultWithPostInfo;20292030 2031 2032 2033 2034 2035 2036 2037 fn approve_from(2038 &self,2039 sender: T::CrossAccountId,2040 from: T::CrossAccountId,2041 to: T::CrossAccountId,2042 token: TokenId,2043 amount: u128,2044 ) -> DispatchResultWithPostInfo;20452046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 fn transfer_from(2057 &self,2058 sender: T::CrossAccountId,2059 from: T::CrossAccountId,2060 to: T::CrossAccountId,2061 token: TokenId,2062 amount: u128,2063 budget: &dyn Budget,2064 ) -> DispatchResultWithPostInfo;20652066 2067 2068 2069 2070 2071 2072 2073 2074 2075 fn burn_from(2076 &self,2077 sender: T::CrossAccountId,2078 from: T::CrossAccountId,2079 token: TokenId,2080 amount: u128,2081 budget: &dyn Budget,2082 ) -> DispatchResultWithPostInfo;20832084 2085 2086 2087 2088 2089 2090 fn check_nesting(2091 &self,2092 sender: T::CrossAccountId,2093 from: (CollectionId, TokenId),2094 under: TokenId,2095 budget: &dyn Budget,2096 ) -> DispatchResult;20972098 2099 2100 2101 2102 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21032104 2105 2106 2107 2108 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21092110 2111 2112 2113 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21142115 2116 fn collection_tokens(&self) -> Vec<TokenId>;21172118 2119 2120 2121 fn token_exists(&self, token: TokenId) -> bool;21222123 2124 fn last_token_id(&self) -> TokenId;21252126 2127 2128 2129 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;21302131 2132 2133 2134 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;21352136 2137 2138 2139 2140 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21412142 2143 2144 2145 2146 2147 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21482149 2150 fn total_supply(&self) -> u32;21512152 2153 2154 2155 fn account_balance(&self, account: T::CrossAccountId) -> u32;21562157 2158 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21592160 2161 fn total_pieces(&self, token: TokenId) -> Option<u128>;21622163 2164 2165 2166 2167 2168 fn allowance(2169 &self,2170 sender: T::CrossAccountId,2171 spender: T::CrossAccountId,2172 token: TokenId,2173 ) -> u128;21742175 2176 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21772178 2179 2180 2181 2182 fn set_allowance_for_all(2183 &self,2184 owner: T::CrossAccountId,2185 operator: T::CrossAccountId,2186 approve: bool,2187 ) -> DispatchResultWithPostInfo;21882189 2190 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21912192 2193 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2194}219521962197pub trait RefungibleExtensions<T>2198where2199 T: Config,2200{2201 2202 2203 2204 2205 2206 2207 2208 fn repartition(2209 &self,2210 sender: &T::CrossAccountId,2211 token: TokenId,2212 amount: u128,2213 ) -> DispatchResultWithPostInfo;2214}22152216221722182219pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2220 let post_info = PostDispatchInfo {2221 actual_weight: Some(weight),2222 pays_fee: Pays::Yes,2223 };2224 match res {2225 Ok(()) => Ok(post_info),2226 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2227 }2228}22292230impl<T: Config> From<PropertiesError> for Error<T> {2231 fn from(error: PropertiesError) -> Self {2232 match error {2233 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2234 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2235 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2236 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2237 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2238 }2239 }2240}