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 pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {395 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)396 }397398 399 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {400 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)401 }402403 404 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {405 ensure!(406 <Allowlist<T>>::get((self.id, user)),407 <Error<T>>::AddressNotInAllowlist408 );409 Ok(())410 }411412 413 414 415 pub fn change_owner(416 &mut self,417 caller: T::CrossAccountId,418 new_owner: T::CrossAccountId,419 ) -> DispatchResult {420 self.check_is_internal()?;421 self.check_is_owner(&caller)?;422 self.collection.owner = new_owner.as_sub().clone();423424 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(425 self.id,426 new_owner.as_sub().clone(),427 ));428 <PalletEvm<T>>::deposit_log(429 erc::CollectionHelpersEvents::CollectionChanged {430 collection_id: eth::collection_id_to_address(self.id),431 }432 .to_log(T::ContractAddress::get()),433 );434435 self.save()436 }437}438439#[frame_support::pallet]440pub mod pallet {441 use super::*;442 use dispatch::CollectionDispatch;443 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};444 use frame_system::pallet_prelude::*;445 use frame_support::traits::Currency;446 use up_data_structs::{TokenId, mapping::TokenAddressMapping};447 use scale_info::TypeInfo;448 use weights::WeightInfo;449450 #[pallet::config]451 pub trait Config:452 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo453 {454 455 type WeightInfo: WeightInfo;456457 458 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;459460 461 type Currency: Currency<Self::AccountId>;462463 464 #[pallet::constant]465 type CollectionCreationPrice: Get<466 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,467 >;468469 470 type CollectionDispatch: CollectionDispatch<Self>;471472 473 type TreasuryAccountId: Get<Self::AccountId>;474475 476 #[pallet::constant]477 type ContractAddress: Get<H160>;478479 480 type EvmTokenAddressMapping: TokenAddressMapping<H160>;481482 483 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;484 }485486 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);487488 #[pallet::pallet]489 #[pallet::storage_version(STORAGE_VERSION)]490 #[pallet::generate_store(pub(super) trait Store)]491 pub struct Pallet<T>(_);492493 #[pallet::extra_constants]494 impl<T: Config> Pallet<T> {495 496 pub fn collection_admins_limit() -> u32 {497 COLLECTION_ADMINS_LIMIT498 }499 }500501 #[pallet::event]502 #[pallet::generate_deposit(pub fn deposit_event)]503 pub enum Event<T: Config> {504 505 CollectionCreated(506 507 CollectionId,508 509 u8,510 511 T::AccountId,512 ),513514 515 CollectionDestroyed(516 517 CollectionId,518 ),519520 521 ItemCreated(522 523 CollectionId,524 525 TokenId,526 527 T::CrossAccountId,528 529 u128,530 ),531532 533 ItemDestroyed(534 535 CollectionId,536 537 TokenId,538 539 T::CrossAccountId,540 541 u128,542 ),543544 545 Transfer(546 547 CollectionId,548 549 TokenId,550 551 T::CrossAccountId,552 553 T::CrossAccountId,554 555 u128,556 ),557558 559 Approved(560 561 CollectionId,562 563 TokenId,564 565 T::CrossAccountId,566 567 T::CrossAccountId,568 569 u128,570 ),571572 573 ApprovedForAll(574 575 CollectionId,576 577 T::CrossAccountId,578 579 T::CrossAccountId,580 581 bool,582 ),583584 585 CollectionPropertySet(586 587 CollectionId,588 589 PropertyKey,590 ),591592 593 CollectionPropertyDeleted(594 595 CollectionId,596 597 PropertyKey,598 ),599600 601 TokenPropertySet(602 603 CollectionId,604 605 TokenId,606 607 PropertyKey,608 ),609610 611 TokenPropertyDeleted(612 613 CollectionId,614 615 TokenId,616 617 PropertyKey,618 ),619620 621 PropertyPermissionSet(622 623 CollectionId,624 625 PropertyKey,626 ),627628 629 AllowListAddressAdded(630 631 CollectionId,632 633 T::CrossAccountId,634 ),635636 637 AllowListAddressRemoved(638 639 CollectionId,640 641 T::CrossAccountId,642 ),643644 645 CollectionAdminAdded(646 647 CollectionId,648 649 T::CrossAccountId,650 ),651652 653 CollectionAdminRemoved(654 655 CollectionId,656 657 T::CrossAccountId,658 ),659660 661 CollectionLimitSet(662 663 CollectionId,664 ),665666 667 CollectionOwnerChanged(668 669 CollectionId,670 671 T::AccountId,672 ),673674 675 CollectionPermissionSet(676 677 CollectionId,678 ),679680 681 CollectionSponsorSet(682 683 CollectionId,684 685 T::AccountId,686 ),687688 689 SponsorshipConfirmed(690 691 CollectionId,692 693 T::AccountId,694 ),695696 697 CollectionSponsorRemoved(698 699 CollectionId,700 ),701 }702703 #[pallet::error]704 pub enum Error<T> {705 706 CollectionNotFound,707 708 MustBeTokenOwner,709 710 NoPermission,711 712 CantDestroyNotEmptyCollection,713 714 PublicMintingNotAllowed,715 716 AddressNotInAllowlist,717718 719 CollectionNameLimitExceeded,720 721 CollectionDescriptionLimitExceeded,722 723 CollectionTokenPrefixLimitExceeded,724 725 TotalCollectionsLimitExceeded,726 727 CollectionAdminCountExceeded,728 729 CollectionLimitBoundsExceeded,730 731 OwnerPermissionsCantBeReverted,732 733 TransferNotAllowed,734 735 AccountTokenLimitExceeded,736 737 CollectionTokenLimitExceeded,738 739 MetadataFlagFrozen,740741 742 TokenNotFound,743 744 TokenValueTooLow,745 746 ApprovedValueTooLow,747 748 CantApproveMoreThanOwned,749 750 AddressIsNotEthMirror,751752 753 AddressIsZero,754755 756 UnsupportedOperation,757758 759 NotSufficientFounds,760761 762 UserIsNotAllowedToNest,763 764 SourceCollectionIsNotAllowedToNest,765766 767 CollectionFieldSizeExceeded,768769 770 NoSpaceForProperty,771772 773 PropertyLimitReached,774775 776 PropertyKeyIsTooLong,777778 779 InvalidCharacterInPropertyKey,780781 782 EmptyPropertyKey,783784 785 CollectionIsExternal,786787 788 CollectionIsInternal,789790 791 ConfirmSponsorshipFail,792793 794 UserIsNotCollectionAdmin,795 }796797 798 #[pallet::storage]799 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;800801 802 #[pallet::storage]803 pub type DestroyedCollectionCount<T> =804 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;805806 807 #[pallet::storage]808 pub type CollectionById<T> = StorageMap<809 Hasher = Blake2_128Concat,810 Key = CollectionId,811 Value = Collection<<T as frame_system::Config>::AccountId>,812 QueryKind = OptionQuery,813 >;814815 816 #[pallet::storage]817 #[pallet::getter(fn collection_properties)]818 pub type CollectionProperties<T> = StorageMap<819 Hasher = Blake2_128Concat,820 Key = CollectionId,821 Value = Properties,822 QueryKind = ValueQuery,823 OnEmpty = up_data_structs::CollectionProperties,824 >;825826 827 #[pallet::storage]828 #[pallet::getter(fn property_permissions)]829 pub type CollectionPropertyPermissions<T> = StorageMap<830 Hasher = Blake2_128Concat,831 Key = CollectionId,832 Value = PropertiesPermissionMap,833 QueryKind = ValueQuery,834 >;835836 837 #[pallet::storage]838 pub type AdminAmount<T> = StorageMap<839 Hasher = Blake2_128Concat,840 Key = CollectionId,841 Value = u32,842 QueryKind = ValueQuery,843 >;844845 846 #[pallet::storage]847 pub type IsAdmin<T: Config> = StorageNMap<848 Key = (849 Key<Blake2_128Concat, CollectionId>,850 Key<Blake2_128Concat, T::CrossAccountId>,851 ),852 Value = bool,853 QueryKind = ValueQuery,854 >;855856 857 #[pallet::storage]858 pub type Allowlist<T: Config> = StorageNMap<859 Key = (860 Key<Blake2_128Concat, CollectionId>,861 Key<Blake2_128Concat, T::CrossAccountId>,862 ),863 Value = bool,864 QueryKind = ValueQuery,865 >;866867 868 #[pallet::storage]869 pub type DummyStorageValue<T: Config> = StorageValue<870 Value = (871 CollectionStats,872 CollectionId,873 TokenId,874 TokenChild,875 PhantomType<(876 TokenData<T::CrossAccountId>,877 RpcCollection<T::AccountId>,878 879 RmrkCollectionInfo<T::AccountId>,880 RmrkInstanceInfo<T::AccountId>,881 RmrkResourceInfo,882 RmrkPropertyInfo,883 RmrkBaseInfo<T::AccountId>,884 RmrkPartType,885 RmrkBoundedTheme,886 RmrkNftChild,887 888 PovInfo,889 )>,890 ),891 QueryKind = OptionQuery,892 >;893894 #[pallet::hooks]895 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {896 fn on_runtime_upgrade() -> Weight {897 StorageVersion::new(1).put::<Pallet<T>>();898899 Weight::zero()900 }901 }902}903904impl<T: Config> Pallet<T> {905 906 907 908 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {909 ensure!(910 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,911 <Error<T>>::AddressIsZero912 );913 Ok(())914 }915916 917 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {918 <IsAdmin<T>>::iter_prefix((collection,))919 .map(|(a, _)| a)920 .collect()921 }922923 924 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {925 <Allowlist<T>>::iter_prefix((collection,))926 .map(|(a, _)| a)927 .collect()928 }929930 931 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {932 <Allowlist<T>>::get((collection, user))933 }934935 936 pub fn collection_stats() -> CollectionStats {937 let created = <CreatedCollectionCount<T>>::get();938 let destroyed = <DestroyedCollectionCount<T>>::get();939 CollectionStats {940 created: created.0,941 destroyed: destroyed.0,942 alive: created.0 - destroyed.0,943 }944 }945946 947 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {948 let collection = <CollectionById<T>>::get(collection)?;949 let limits = collection.limits;950 let effective_limits = CollectionLimits {951 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),952 sponsored_data_size: Some(limits.sponsored_data_size()),953 sponsored_data_rate_limit: Some(954 limits955 .sponsored_data_rate_limit956 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),957 ),958 token_limit: Some(limits.token_limit()),959 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(960 match collection.mode {961 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,962 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,963 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,964 },965 )),966 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),967 owner_can_transfer: Some(limits.owner_can_transfer()),968 owner_can_destroy: Some(limits.owner_can_destroy()),969 transfers_enabled: Some(limits.transfers_enabled()),970 };971972 Some(effective_limits)973 }974975 976 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {977 let Collection {978 name,979 description,980 owner,981 mode,982 token_prefix,983 sponsorship,984 limits,985 permissions,986 flags,987 } = <CollectionById<T>>::get(collection)?;988989 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)990 .into_iter()991 .map(|(key, permission)| PropertyKeyPermission { key, permission })992 .collect();993994 let properties = <CollectionProperties<T>>::get(collection)995 .into_iter()996 .map(|(key, value)| Property { key, value })997 .collect();998999 let permissions = CollectionPermissions {1000 access: Some(permissions.access()),1001 mint_mode: Some(permissions.mint_mode()),1002 nesting: Some(permissions.nesting().clone()),1003 };10041005 Some(RpcCollection {1006 name: name.into_inner(),1007 description: description.into_inner(),1008 owner,1009 mode,1010 token_prefix: token_prefix.into_inner(),1011 sponsorship,1012 limits,1013 permissions,1014 token_property_permissions,1015 properties,1016 read_only: flags.external,10171018 flags: RpcCollectionFlags {1019 foreign: flags.foreign,1020 erc721metadata: flags.erc721metadata,1021 },1022 })1023 }1024}10251026macro_rules! limit_default {1027 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1028 $(1029 if let Some($new) = $new.$field {1030 let $old = $old.$field($($arg)?);1031 let _ = $new;1032 let _ = $old;1033 $check1034 } else {1035 $new.$field = $old.$field1036 }1037 )*1038 }};1039}1040macro_rules! limit_default_clone {1041 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1042 $(1043 if let Some($new) = $new.$field.clone() {1044 let $old = $old.$field($($arg)?);1045 let _ = $new;1046 let _ = $old;1047 $check1048 } else {1049 $new.$field = $old.$field.clone()1050 }1051 )*1052 }};1053}10541055impl<T: Config> Pallet<T> {1056 1057 1058 1059 1060 1061 pub fn init_collection(1062 owner: T::CrossAccountId,1063 payer: T::CrossAccountId,1064 data: CreateCollectionData<T::AccountId>,1065 flags: CollectionFlags,1066 ) -> Result<CollectionId, DispatchError> {1067 {1068 ensure!(1069 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1070 Error::<T>::CollectionTokenPrefixLimitExceeded1071 );1072 }10731074 let created_count = <CreatedCollectionCount<T>>::get()1075 .01076 .checked_add(1)1077 .ok_or(ArithmeticError::Overflow)?;1078 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1079 let id = CollectionId(created_count);10801081 1082 ensure!(1083 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1084 <Error<T>>::TotalCollectionsLimitExceeded1085 );10861087 10881089 let collection = Collection {1090 owner: owner.as_sub().clone(),1091 name: data.name,1092 mode: data.mode.clone(),1093 description: data.description,1094 token_prefix: data.token_prefix,1095 sponsorship: data1096 .pending_sponsor1097 .map(SponsorshipState::Unconfirmed)1098 .unwrap_or_default(),1099 limits: data1100 .limits1101 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1102 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1103 permissions: data1104 .permissions1105 .map(|permissions| {1106 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1107 })1108 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1109 flags,1110 };11111112 let mut collection_properties = up_data_structs::CollectionProperties::get();1113 collection_properties1114 .try_set_from_iter(data.properties.into_iter())1115 .map_err(<Error<T>>::from)?;11161117 CollectionProperties::<T>::insert(id, collection_properties);11181119 let mut token_props_permissions = PropertiesPermissionMap::new();1120 token_props_permissions1121 .try_set_from_iter(data.token_property_permissions.into_iter())1122 .map_err(<Error<T>>::from)?;11231124 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11251126 1127 {1128 let mut imbalance =1129 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1130 imbalance.subsume(1131 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1132 &T::TreasuryAccountId::get(),1133 T::CollectionCreationPrice::get(),1134 ),1135 );1136 <T as Config>::Currency::settle(1137 payer.as_sub(),1138 imbalance,1139 WithdrawReasons::TRANSFER,1140 ExistenceRequirement::KeepAlive,1141 )1142 .map_err(|_| Error::<T>::NotSufficientFounds)?;1143 }11441145 <CreatedCollectionCount<T>>::put(created_count);1146 <Pallet<T>>::deposit_event(Event::CollectionCreated(1147 id,1148 data.mode.id(),1149 owner.as_sub().clone(),1150 ));1151 <PalletEvm<T>>::deposit_log(1152 erc::CollectionHelpersEvents::CollectionCreated {1153 owner: *owner.as_eth(),1154 collection_id: eth::collection_id_to_address(id),1155 }1156 .to_log(T::ContractAddress::get()),1157 );1158 <CollectionById<T>>::insert(id, collection);1159 Ok(id)1160 }11611162 1163 1164 1165 1166 pub fn destroy_collection(1167 collection: CollectionHandle<T>,1168 sender: &T::CrossAccountId,1169 ) -> DispatchResult {1170 ensure!(1171 collection.limits.owner_can_destroy(),1172 <Error<T>>::NoPermission,1173 );1174 collection.check_is_owner(sender)?;11751176 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1177 .01178 .checked_add(1)1179 .ok_or(ArithmeticError::Overflow)?;11801181 11821183 <DestroyedCollectionCount<T>>::put(destroyed_collections);1184 <CollectionById<T>>::remove(collection.id);1185 <AdminAmount<T>>::remove(collection.id);1186 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1187 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1188 <CollectionProperties<T>>::remove(collection.id);11891190 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11911192 <PalletEvm<T>>::deposit_log(1193 erc::CollectionHelpersEvents::CollectionDestroyed {1194 collection_id: eth::collection_id_to_address(collection.id),1195 }1196 .to_log(T::ContractAddress::get()),1197 );1198 Ok(())1199 }12001201 fn modify_collection_properties(1202 collection: &CollectionHandle<T>,1203 sender: &T::CrossAccountId,1204 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1205 ) -> DispatchResult {1206 collection.check_is_owner_or_admin(sender)?;12071208 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12091210 for (key, value) in properties_updates {1211 match value {1212 Some(value) => {1213 stored_properties1214 .try_set(key.clone(), value)1215 .map_err(<Error<T>>::from)?;12161217 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1218 <PalletEvm<T>>::deposit_log(1219 erc::CollectionHelpersEvents::CollectionChanged {1220 collection_id: eth::collection_id_to_address(collection.id),1221 }1222 .to_log(T::ContractAddress::get()),1223 );1224 }1225 None => {1226 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12271228 Self::deposit_event(Event::CollectionPropertyDeleted(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 }1237 }12381239 <CollectionProperties<T>>::mutate(collection.id, |properties| {1240 *properties = stored_properties;1241 });12421243 Ok(())1244 }12451246 1247 1248 1249 1250 1251 pub fn set_collection_property(1252 collection: &CollectionHandle<T>,1253 sender: &T::CrossAccountId,1254 property: Property,1255 ) -> DispatchResult {1256 Self::set_collection_properties(collection, sender, [property].into_iter())1257 }12581259 1260 1261 1262 1263 1264 1265 pub fn set_scoped_collection_property(1266 collection_id: CollectionId,1267 scope: PropertyScope,1268 property: Property,1269 ) -> DispatchResult {1270 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1271 properties.try_scoped_set(scope, property.key, property.value)1272 })1273 .map_err(<Error<T>>::from)?;12741275 Ok(())1276 }12771278 1279 1280 1281 1282 1283 1284 pub fn set_scoped_collection_properties(1285 collection_id: CollectionId,1286 scope: PropertyScope,1287 properties: impl Iterator<Item = Property>,1288 ) -> DispatchResult {1289 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1290 stored_properties.try_scoped_set_from_iter(scope, properties)1291 })1292 .map_err(<Error<T>>::from)?;12931294 Ok(())1295 }12961297 1298 1299 1300 1301 1302 #[transactional]1303 pub fn set_collection_properties(1304 collection: &CollectionHandle<T>,1305 sender: &T::CrossAccountId,1306 properties: impl Iterator<Item = Property>,1307 ) -> DispatchResult {1308 Self::modify_collection_properties(1309 collection,1310 sender,1311 properties.map(|property| (property.key, Some(property.value))),1312 )1313 }13141315 1316 1317 1318 1319 1320 pub fn delete_collection_property(1321 collection: &CollectionHandle<T>,1322 sender: &T::CrossAccountId,1323 property_key: PropertyKey,1324 ) -> DispatchResult {1325 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1326 }13271328 1329 1330 1331 1332 1333 #[transactional]1334 pub fn delete_collection_properties(1335 collection: &CollectionHandle<T>,1336 sender: &T::CrossAccountId,1337 property_keys: impl Iterator<Item = PropertyKey>,1338 ) -> DispatchResult {1339 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1340 }13411342 1343 1344 1345 1346 1347 1348 pub fn set_property_permission_unchecked(1349 collection: CollectionId,1350 property_permission: PropertyKeyPermission,1351 ) -> DispatchResult {1352 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1353 permissions.try_set(property_permission.key, property_permission.permission)1354 })1355 .map_err(<Error<T>>::from)?;1356 Ok(())1357 }13581359 1360 1361 1362 1363 1364 pub fn set_property_permission(1365 collection: &CollectionHandle<T>,1366 sender: &T::CrossAccountId,1367 property_permission: PropertyKeyPermission,1368 ) -> DispatchResult {1369 Self::set_scoped_property_permission(1370 collection,1371 sender,1372 PropertyScope::None,1373 property_permission,1374 )1375 }13761377 1378 1379 1380 1381 1382 1383 pub fn set_scoped_property_permission(1384 collection: &CollectionHandle<T>,1385 sender: &T::CrossAccountId,1386 scope: PropertyScope,1387 property_permission: PropertyKeyPermission,1388 ) -> DispatchResult {1389 collection.check_is_owner_or_admin(sender)?;13901391 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1392 let current_permission = all_permissions.get(&property_permission.key);1393 if matches![1394 current_permission,1395 Some(PropertyPermission { mutable: false, .. })1396 ] {1397 return Err(<Error<T>>::NoPermission.into());1398 }13991400 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1401 let property_permission = property_permission.clone();1402 permissions.try_scoped_set(1403 scope,1404 property_permission.key,1405 property_permission.permission,1406 )1407 })1408 .map_err(<Error<T>>::from)?;14091410 Self::deposit_event(Event::PropertyPermissionSet(1411 collection.id,1412 property_permission.key,1413 ));1414 <PalletEvm<T>>::deposit_log(1415 erc::CollectionHelpersEvents::CollectionChanged {1416 collection_id: eth::collection_id_to_address(collection.id),1417 }1418 .to_log(T::ContractAddress::get()),1419 );14201421 Ok(())1422 }14231424 1425 1426 1427 1428 1429 #[transactional]1430 pub fn set_token_property_permissions(1431 collection: &CollectionHandle<T>,1432 sender: &T::CrossAccountId,1433 property_permissions: Vec<PropertyKeyPermission>,1434 ) -> DispatchResult {1435 Self::set_scoped_token_property_permissions(1436 collection,1437 sender,1438 PropertyScope::None,1439 property_permissions,1440 )1441 }14421443 1444 1445 1446 1447 1448 1449 #[transactional]1450 pub fn set_scoped_token_property_permissions(1451 collection: &CollectionHandle<T>,1452 sender: &T::CrossAccountId,1453 scope: PropertyScope,1454 property_permissions: Vec<PropertyKeyPermission>,1455 ) -> DispatchResult {1456 for prop_pemission in property_permissions {1457 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1458 }14591460 Ok(())1461 }14621463 1464 pub fn get_collection_property(1465 collection_id: CollectionId,1466 key: &PropertyKey,1467 ) -> Option<PropertyValue> {1468 Self::collection_properties(collection_id).get(key).cloned()1469 }14701471 1472 pub fn bytes_keys_to_property_keys(1473 keys: Vec<Vec<u8>>,1474 ) -> Result<Vec<PropertyKey>, DispatchError> {1475 keys.into_iter()1476 .map(|key| -> Result<PropertyKey, DispatchError> {1477 key.try_into()1478 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1479 })1480 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1481 }14821483 1484 pub fn filter_collection_properties(1485 collection_id: CollectionId,1486 keys: Option<Vec<PropertyKey>>,1487 ) -> Result<Vec<Property>, DispatchError> {1488 let properties = Self::collection_properties(collection_id);14891490 let properties = keys1491 .map(|keys| {1492 keys.into_iter()1493 .filter_map(|key| {1494 properties.get(&key).map(|value| Property {1495 key,1496 value: value.clone(),1497 })1498 })1499 .collect()1500 })1501 .unwrap_or_else(|| {1502 properties1503 .into_iter()1504 .map(|(key, value)| Property { key, value })1505 .collect()1506 });15071508 Ok(properties)1509 }15101511 1512 pub fn filter_property_permissions(1513 collection_id: CollectionId,1514 keys: Option<Vec<PropertyKey>>,1515 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1516 let permissions = Self::property_permissions(collection_id);15171518 let key_permissions = keys1519 .map(|keys| {1520 keys.into_iter()1521 .filter_map(|key| {1522 permissions1523 .get(&key)1524 .map(|permission| PropertyKeyPermission {1525 key,1526 permission: permission.clone(),1527 })1528 })1529 .collect()1530 })1531 .unwrap_or_else(|| {1532 permissions1533 .into_iter()1534 .map(|(key, permission)| PropertyKeyPermission { key, permission })1535 .collect()1536 });15371538 Ok(key_permissions)1539 }15401541 1542 1543 1544 pub fn toggle_allowlist(1545 collection: &CollectionHandle<T>,1546 sender: &T::CrossAccountId,1547 user: &T::CrossAccountId,1548 allowed: bool,1549 ) -> DispatchResult {1550 collection.check_is_owner_or_admin(sender)?;15511552 15531554 if allowed {1555 <Allowlist<T>>::insert((collection.id, user), true);1556 Self::deposit_event(Event::<T>::AllowListAddressAdded(1557 collection.id,1558 user.clone(),1559 ));1560 } else {1561 <Allowlist<T>>::remove((collection.id, user));1562 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1563 collection.id,1564 user.clone(),1565 ));1566 }15671568 <PalletEvm<T>>::deposit_log(1569 erc::CollectionHelpersEvents::CollectionChanged {1570 collection_id: eth::collection_id_to_address(collection.id),1571 }1572 .to_log(T::ContractAddress::get()),1573 );15741575 Ok(())1576 }15771578 1579 1580 1581 pub fn toggle_admin(1582 collection: &CollectionHandle<T>,1583 sender: &T::CrossAccountId,1584 user: &T::CrossAccountId,1585 admin: bool,1586 ) -> DispatchResult {1587 collection.check_is_internal()?;1588 collection.check_is_owner(sender)?;15891590 let is_admin = <IsAdmin<T>>::get((collection.id, user));1591 if is_admin == admin {1592 if admin {1593 return Ok(());1594 } else {1595 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1596 }1597 }1598 let amount = <AdminAmount<T>>::get(collection.id);15991600 16011602 if admin {1603 let amount = amount1604 .checked_add(1)1605 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1606 ensure!(1607 amount <= Self::collection_admins_limit(),1608 <Error<T>>::CollectionAdminCountExceeded,1609 );16101611 <AdminAmount<T>>::insert(collection.id, amount);1612 <IsAdmin<T>>::insert((collection.id, user), true);16131614 Self::deposit_event(Event::<T>::CollectionAdminAdded(1615 collection.id,1616 user.clone(),1617 ));1618 } else {1619 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1620 <IsAdmin<T>>::remove((collection.id, user));16211622 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1623 collection.id,1624 user.clone(),1625 ));1626 }16271628 <PalletEvm<T>>::deposit_log(1629 erc::CollectionHelpersEvents::CollectionChanged {1630 collection_id: eth::collection_id_to_address(collection.id),1631 }1632 .to_log(T::ContractAddress::get()),1633 );16341635 Ok(())1636 }16371638 1639 pub fn update_limits(1640 user: &T::CrossAccountId,1641 collection: &mut CollectionHandle<T>,1642 new_limit: CollectionLimits,1643 ) -> DispatchResult {1644 collection.check_is_internal()?;1645 collection.check_is_owner_or_admin(user)?;16461647 collection.limits =1648 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16491650 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1651 <PalletEvm<T>>::deposit_log(1652 erc::CollectionHelpersEvents::CollectionChanged {1653 collection_id: eth::collection_id_to_address(collection.id),1654 }1655 .to_log(T::ContractAddress::get()),1656 );16571658 collection.save()1659 }16601661 1662 fn clamp_limits(1663 mode: CollectionMode,1664 old_limit: &CollectionLimits,1665 mut new_limit: CollectionLimits,1666 ) -> Result<CollectionLimits, DispatchError> {1667 let limits = old_limit;1668 limit_default!(old_limit, new_limit,1669 account_token_ownership_limit => ensure!(1670 new_limit <= MAX_TOKEN_OWNERSHIP,1671 <Error<T>>::CollectionLimitBoundsExceeded,1672 ),1673 sponsored_data_size => ensure!(1674 new_limit <= CUSTOM_DATA_LIMIT,1675 <Error<T>>::CollectionLimitBoundsExceeded,1676 ),16771678 sponsored_data_rate_limit => {},1679 token_limit => ensure!(1680 old_limit >= new_limit && new_limit > 0,1681 <Error<T>>::CollectionTokenLimitExceeded1682 ),16831684 sponsor_transfer_timeout(match mode {1685 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1686 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1687 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1688 }) => ensure!(1689 new_limit <= MAX_SPONSOR_TIMEOUT,1690 <Error<T>>::CollectionLimitBoundsExceeded,1691 ),1692 sponsor_approve_timeout => {},1693 owner_can_transfer => ensure!(1694 !limits.owner_can_transfer_instaled() ||1695 old_limit || !new_limit,1696 <Error<T>>::OwnerPermissionsCantBeReverted,1697 ),1698 owner_can_destroy => ensure!(1699 old_limit || !new_limit,1700 <Error<T>>::OwnerPermissionsCantBeReverted,1701 ),1702 transfers_enabled => {},1703 );1704 Ok(new_limit)1705 }17061707 1708 pub fn update_permissions(1709 user: &T::CrossAccountId,1710 collection: &mut CollectionHandle<T>,1711 new_permission: CollectionPermissions,1712 ) -> DispatchResult {1713 collection.check_is_internal()?;1714 collection.check_is_owner_or_admin(user)?;1715 collection.permissions = Self::clamp_permissions(1716 collection.mode.clone(),1717 &collection.permissions,1718 new_permission,1719 )?;17201721 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1722 <PalletEvm<T>>::deposit_log(1723 erc::CollectionHelpersEvents::CollectionChanged {1724 collection_id: eth::collection_id_to_address(collection.id),1725 }1726 .to_log(T::ContractAddress::get()),1727 );17281729 collection.save()1730 }17311732 1733 fn clamp_permissions(1734 _mode: CollectionMode,1735 old_permission: &CollectionPermissions,1736 mut new_permission: CollectionPermissions,1737 ) -> Result<CollectionPermissions, DispatchError> {1738 limit_default_clone!(old_permission, new_permission,1739 access => {},1740 mint_mode => {},1741 nesting => { },1742 );1743 Ok(new_permission)1744 }17451746 1747 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1748 CollectionProperties::<T>::mutate(collection_id, |properties| {1749 properties.recompute_consumed_space();1750 });17511752 Ok(())1753 }1754}175517561757#[macro_export]1758macro_rules! unsupported {1759 ($runtime:path) => {1760 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1761 };1762}176317641765pub trait CommonWeightInfo<CrossAccountId> {1766 1767 fn create_item() -> Weight;17681769 1770 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17711772 1773 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17741775 1776 fn burn_item() -> Weight;17771778 1779 1780 1781 fn set_collection_properties(amount: u32) -> Weight;17821783 1784 1785 1786 fn delete_collection_properties(amount: u32) -> Weight;17871788 1789 1790 1791 fn set_token_properties(amount: u32) -> Weight;17921793 1794 1795 1796 fn delete_token_properties(amount: u32) -> Weight;17971798 1799 1800 1801 fn set_token_property_permissions(amount: u32) -> Weight;18021803 1804 fn transfer() -> Weight;18051806 1807 fn approve() -> Weight;18081809 1810 fn approve_from() -> Weight;18111812 1813 fn transfer_from() -> Weight;18141815 1816 fn burn_from() -> Weight;18171818 1819 1820 1821 1822 fn burn_recursively_self_raw() -> Weight;18231824 1825 1826 1827 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18281829 1830 1831 1832 1833 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1834 Self::burn_recursively_self_raw()1835 .saturating_mul(max_selfs.max(1) as u64)1836 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1837 }18381839 1840 fn token_owner() -> Weight;18411842 1843 fn set_allowance_for_all() -> Weight;18441845 1846 fn force_repair_item() -> Weight;1847}184818491850pub trait RefungibleExtensionsWeightInfo {1851 1852 fn repartition() -> Weight;1853}185418551856185718581859pub trait CommonCollectionOperations<T: Config> {1860 1861 1862 1863 1864 1865 1866 fn create_item(1867 &self,1868 sender: T::CrossAccountId,1869 to: T::CrossAccountId,1870 data: CreateItemData,1871 nesting_budget: &dyn Budget,1872 ) -> DispatchResultWithPostInfo;18731874 1875 1876 1877 1878 1879 1880 fn create_multiple_items(1881 &self,1882 sender: T::CrossAccountId,1883 to: T::CrossAccountId,1884 data: Vec<CreateItemData>,1885 nesting_budget: &dyn Budget,1886 ) -> DispatchResultWithPostInfo;18871888 1889 1890 1891 1892 1893 1894 fn create_multiple_items_ex(1895 &self,1896 sender: T::CrossAccountId,1897 data: CreateItemExData<T::CrossAccountId>,1898 nesting_budget: &dyn Budget,1899 ) -> DispatchResultWithPostInfo;19001901 1902 1903 1904 1905 1906 fn burn_item(1907 &self,1908 sender: T::CrossAccountId,1909 token: TokenId,1910 amount: u128,1911 ) -> DispatchResultWithPostInfo;19121913 1914 1915 1916 1917 1918 1919 fn burn_item_recursively(1920 &self,1921 sender: T::CrossAccountId,1922 token: TokenId,1923 self_budget: &dyn Budget,1924 breadth_budget: &dyn Budget,1925 ) -> DispatchResultWithPostInfo;19261927 1928 1929 1930 1931 fn set_collection_properties(1932 &self,1933 sender: T::CrossAccountId,1934 properties: Vec<Property>,1935 ) -> DispatchResultWithPostInfo;19361937 1938 1939 1940 1941 fn delete_collection_properties(1942 &self,1943 sender: &T::CrossAccountId,1944 property_keys: Vec<PropertyKey>,1945 ) -> DispatchResultWithPostInfo;19461947 1948 1949 1950 1951 1952 1953 1954 1955 1956 fn set_token_properties(1957 &self,1958 sender: T::CrossAccountId,1959 token_id: TokenId,1960 properties: Vec<Property>,1961 budget: &dyn Budget,1962 ) -> DispatchResultWithPostInfo;19631964 1965 1966 1967 1968 1969 1970 1971 1972 1973 fn delete_token_properties(1974 &self,1975 sender: T::CrossAccountId,1976 token_id: TokenId,1977 property_keys: Vec<PropertyKey>,1978 budget: &dyn Budget,1979 ) -> DispatchResultWithPostInfo;19801981 1982 1983 1984 1985 1986 1987 fn set_token_property_permissions(1988 &self,1989 sender: &T::CrossAccountId,1990 property_permissions: Vec<PropertyKeyPermission>,1991 ) -> DispatchResultWithPostInfo;19921993 1994 1995 1996 1997 1998 1999 2000 fn transfer(2001 &self,2002 sender: T::CrossAccountId,2003 to: T::CrossAccountId,2004 token: TokenId,2005 amount: u128,2006 budget: &dyn Budget,2007 ) -> DispatchResultWithPostInfo;20082009 2010 2011 2012 2013 2014 2015 fn approve(2016 &self,2017 sender: T::CrossAccountId,2018 spender: T::CrossAccountId,2019 token: TokenId,2020 amount: u128,2021 ) -> DispatchResultWithPostInfo;20222023 2024 2025 2026 2027 2028 2029 2030 fn approve_from(2031 &self,2032 sender: T::CrossAccountId,2033 from: T::CrossAccountId,2034 to: T::CrossAccountId,2035 token: TokenId,2036 amount: u128,2037 ) -> DispatchResultWithPostInfo;20382039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 fn transfer_from(2050 &self,2051 sender: T::CrossAccountId,2052 from: T::CrossAccountId,2053 to: T::CrossAccountId,2054 token: TokenId,2055 amount: u128,2056 budget: &dyn Budget,2057 ) -> DispatchResultWithPostInfo;20582059 2060 2061 2062 2063 2064 2065 2066 2067 2068 fn burn_from(2069 &self,2070 sender: T::CrossAccountId,2071 from: T::CrossAccountId,2072 token: TokenId,2073 amount: u128,2074 budget: &dyn Budget,2075 ) -> DispatchResultWithPostInfo;20762077 2078 2079 2080 2081 2082 2083 fn check_nesting(2084 &self,2085 sender: T::CrossAccountId,2086 from: (CollectionId, TokenId),2087 under: TokenId,2088 budget: &dyn Budget,2089 ) -> DispatchResult;20902091 2092 2093 2094 2095 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20962097 2098 2099 2100 2101 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21022103 2104 2105 2106 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21072108 2109 fn collection_tokens(&self) -> Vec<TokenId>;21102111 2112 2113 2114 fn token_exists(&self, token: TokenId) -> bool;21152116 2117 fn last_token_id(&self) -> TokenId;21182119 2120 2121 2122 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;21232124 2125 2126 2127 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;21282129 2130 2131 2132 2133 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21342135 2136 2137 2138 2139 2140 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21412142 2143 fn total_supply(&self) -> u32;21442145 2146 2147 2148 fn account_balance(&self, account: T::CrossAccountId) -> u32;21492150 2151 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21522153 2154 fn total_pieces(&self, token: TokenId) -> Option<u128>;21552156 2157 2158 2159 2160 2161 fn allowance(2162 &self,2163 sender: T::CrossAccountId,2164 spender: T::CrossAccountId,2165 token: TokenId,2166 ) -> u128;21672168 2169 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21702171 2172 2173 2174 2175 fn set_allowance_for_all(2176 &self,2177 owner: T::CrossAccountId,2178 operator: T::CrossAccountId,2179 approve: bool,2180 ) -> DispatchResultWithPostInfo;21812182 2183 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21842185 2186 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2187}218821892190pub trait RefungibleExtensions<T>2191where2192 T: Config,2193{2194 2195 2196 2197 2198 2199 2200 2201 fn repartition(2202 &self,2203 sender: &T::CrossAccountId,2204 token: TokenId,2205 amount: u128,2206 ) -> DispatchResultWithPostInfo;2207}22082209221022112212pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2213 let post_info = PostDispatchInfo {2214 actual_weight: Some(weight),2215 pays_fee: Pays::Yes,2216 };2217 match res {2218 Ok(()) => Ok(post_info),2219 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2220 }2221}22222223impl<T: Config> From<PropertiesError> for Error<T> {2224 fn from(error: PropertiesError) -> Self {2225 match error {2226 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2227 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2228 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2229 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2230 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2231 }2232 }2233}