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>>::set(collection.id, stored_properties);12401241 Ok(())1242 }12431244 1245 1246 1247 1248 1249 pub fn set_collection_property(1250 collection: &CollectionHandle<T>,1251 sender: &T::CrossAccountId,1252 property: Property,1253 ) -> DispatchResult {1254 Self::set_collection_properties(collection, sender, [property].into_iter())1255 }12561257 1258 1259 1260 1261 1262 1263 pub fn set_scoped_collection_property(1264 collection_id: CollectionId,1265 scope: PropertyScope,1266 property: Property,1267 ) -> DispatchResult {1268 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1269 properties.try_scoped_set(scope, property.key, property.value)1270 })1271 .map_err(<Error<T>>::from)?;12721273 Ok(())1274 }12751276 1277 1278 1279 1280 1281 1282 pub fn set_scoped_collection_properties(1283 collection_id: CollectionId,1284 scope: PropertyScope,1285 properties: impl Iterator<Item = Property>,1286 ) -> DispatchResult {1287 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1288 stored_properties.try_scoped_set_from_iter(scope, properties)1289 })1290 .map_err(<Error<T>>::from)?;12911292 Ok(())1293 }12941295 1296 1297 1298 1299 1300 #[transactional]1301 pub fn set_collection_properties(1302 collection: &CollectionHandle<T>,1303 sender: &T::CrossAccountId,1304 properties: impl Iterator<Item = Property>,1305 ) -> DispatchResult {1306 Self::modify_collection_properties(1307 collection,1308 sender,1309 properties.map(|property| (property.key, Some(property.value))),1310 )1311 }13121313 1314 1315 1316 1317 1318 pub fn delete_collection_property(1319 collection: &CollectionHandle<T>,1320 sender: &T::CrossAccountId,1321 property_key: PropertyKey,1322 ) -> DispatchResult {1323 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1324 }13251326 1327 1328 1329 1330 1331 #[transactional]1332 pub fn delete_collection_properties(1333 collection: &CollectionHandle<T>,1334 sender: &T::CrossAccountId,1335 property_keys: impl Iterator<Item = PropertyKey>,1336 ) -> DispatchResult {1337 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1338 }13391340 1341 1342 1343 1344 1345 1346 pub fn set_property_permission_unchecked(1347 collection: CollectionId,1348 property_permission: PropertyKeyPermission,1349 ) -> DispatchResult {1350 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1351 permissions.try_set(property_permission.key, property_permission.permission)1352 })1353 .map_err(<Error<T>>::from)?;1354 Ok(())1355 }13561357 1358 1359 1360 1361 1362 pub fn set_property_permission(1363 collection: &CollectionHandle<T>,1364 sender: &T::CrossAccountId,1365 property_permission: PropertyKeyPermission,1366 ) -> DispatchResult {1367 Self::set_scoped_property_permission(1368 collection,1369 sender,1370 PropertyScope::None,1371 property_permission,1372 )1373 }13741375 1376 1377 1378 1379 1380 1381 pub fn set_scoped_property_permission(1382 collection: &CollectionHandle<T>,1383 sender: &T::CrossAccountId,1384 scope: PropertyScope,1385 property_permission: PropertyKeyPermission,1386 ) -> DispatchResult {1387 collection.check_is_owner_or_admin(sender)?;13881389 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1390 let current_permission = all_permissions.get(&property_permission.key);1391 if matches![1392 current_permission,1393 Some(PropertyPermission { mutable: false, .. })1394 ] {1395 return Err(<Error<T>>::NoPermission.into());1396 }13971398 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1399 let property_permission = property_permission.clone();1400 permissions.try_scoped_set(1401 scope,1402 property_permission.key,1403 property_permission.permission,1404 )1405 })1406 .map_err(<Error<T>>::from)?;14071408 Self::deposit_event(Event::PropertyPermissionSet(1409 collection.id,1410 property_permission.key,1411 ));1412 <PalletEvm<T>>::deposit_log(1413 erc::CollectionHelpersEvents::CollectionChanged {1414 collection_id: eth::collection_id_to_address(collection.id),1415 }1416 .to_log(T::ContractAddress::get()),1417 );14181419 Ok(())1420 }14211422 1423 1424 1425 1426 1427 #[transactional]1428 pub fn set_token_property_permissions(1429 collection: &CollectionHandle<T>,1430 sender: &T::CrossAccountId,1431 property_permissions: Vec<PropertyKeyPermission>,1432 ) -> DispatchResult {1433 Self::set_scoped_token_property_permissions(1434 collection,1435 sender,1436 PropertyScope::None,1437 property_permissions,1438 )1439 }14401441 1442 1443 1444 1445 1446 1447 #[transactional]1448 pub fn set_scoped_token_property_permissions(1449 collection: &CollectionHandle<T>,1450 sender: &T::CrossAccountId,1451 scope: PropertyScope,1452 property_permissions: Vec<PropertyKeyPermission>,1453 ) -> DispatchResult {1454 for prop_pemission in property_permissions {1455 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1456 }14571458 Ok(())1459 }14601461 1462 pub fn get_collection_property(1463 collection_id: CollectionId,1464 key: &PropertyKey,1465 ) -> Option<PropertyValue> {1466 Self::collection_properties(collection_id).get(key).cloned()1467 }14681469 1470 pub fn bytes_keys_to_property_keys(1471 keys: Vec<Vec<u8>>,1472 ) -> Result<Vec<PropertyKey>, DispatchError> {1473 keys.into_iter()1474 .map(|key| -> Result<PropertyKey, DispatchError> {1475 key.try_into()1476 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1477 })1478 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1479 }14801481 1482 pub fn filter_collection_properties(1483 collection_id: CollectionId,1484 keys: Option<Vec<PropertyKey>>,1485 ) -> Result<Vec<Property>, DispatchError> {1486 let properties = Self::collection_properties(collection_id);14871488 let properties = keys1489 .map(|keys| {1490 keys.into_iter()1491 .filter_map(|key| {1492 properties.get(&key).map(|value| Property {1493 key,1494 value: value.clone(),1495 })1496 })1497 .collect()1498 })1499 .unwrap_or_else(|| {1500 properties1501 .into_iter()1502 .map(|(key, value)| Property { key, value })1503 .collect()1504 });15051506 Ok(properties)1507 }15081509 1510 pub fn filter_property_permissions(1511 collection_id: CollectionId,1512 keys: Option<Vec<PropertyKey>>,1513 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1514 let permissions = Self::property_permissions(collection_id);15151516 let key_permissions = keys1517 .map(|keys| {1518 keys.into_iter()1519 .filter_map(|key| {1520 permissions1521 .get(&key)1522 .map(|permission| PropertyKeyPermission {1523 key,1524 permission: permission.clone(),1525 })1526 })1527 .collect()1528 })1529 .unwrap_or_else(|| {1530 permissions1531 .into_iter()1532 .map(|(key, permission)| PropertyKeyPermission { key, permission })1533 .collect()1534 });15351536 Ok(key_permissions)1537 }15381539 1540 1541 1542 pub fn toggle_allowlist(1543 collection: &CollectionHandle<T>,1544 sender: &T::CrossAccountId,1545 user: &T::CrossAccountId,1546 allowed: bool,1547 ) -> DispatchResult {1548 collection.check_is_owner_or_admin(sender)?;15491550 15511552 if allowed {1553 <Allowlist<T>>::insert((collection.id, user), true);1554 Self::deposit_event(Event::<T>::AllowListAddressAdded(1555 collection.id,1556 user.clone(),1557 ));1558 } else {1559 <Allowlist<T>>::remove((collection.id, user));1560 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1561 collection.id,1562 user.clone(),1563 ));1564 }15651566 <PalletEvm<T>>::deposit_log(1567 erc::CollectionHelpersEvents::CollectionChanged {1568 collection_id: eth::collection_id_to_address(collection.id),1569 }1570 .to_log(T::ContractAddress::get()),1571 );15721573 Ok(())1574 }15751576 1577 1578 1579 pub fn toggle_admin(1580 collection: &CollectionHandle<T>,1581 sender: &T::CrossAccountId,1582 user: &T::CrossAccountId,1583 admin: bool,1584 ) -> DispatchResult {1585 collection.check_is_internal()?;1586 collection.check_is_owner(sender)?;15871588 let is_admin = <IsAdmin<T>>::get((collection.id, user));1589 if is_admin == admin {1590 if admin {1591 return Ok(());1592 } else {1593 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1594 }1595 }1596 let amount = <AdminAmount<T>>::get(collection.id);15971598 15991600 if admin {1601 let amount = amount1602 .checked_add(1)1603 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1604 ensure!(1605 amount <= Self::collection_admins_limit(),1606 <Error<T>>::CollectionAdminCountExceeded,1607 );16081609 <AdminAmount<T>>::insert(collection.id, amount);1610 <IsAdmin<T>>::insert((collection.id, user), true);16111612 Self::deposit_event(Event::<T>::CollectionAdminAdded(1613 collection.id,1614 user.clone(),1615 ));1616 } else {1617 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1618 <IsAdmin<T>>::remove((collection.id, user));16191620 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1621 collection.id,1622 user.clone(),1623 ));1624 }16251626 <PalletEvm<T>>::deposit_log(1627 erc::CollectionHelpersEvents::CollectionChanged {1628 collection_id: eth::collection_id_to_address(collection.id),1629 }1630 .to_log(T::ContractAddress::get()),1631 );16321633 Ok(())1634 }16351636 1637 pub fn update_limits(1638 user: &T::CrossAccountId,1639 collection: &mut CollectionHandle<T>,1640 new_limit: CollectionLimits,1641 ) -> DispatchResult {1642 collection.check_is_internal()?;1643 collection.check_is_owner_or_admin(user)?;16441645 collection.limits =1646 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16471648 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1649 <PalletEvm<T>>::deposit_log(1650 erc::CollectionHelpersEvents::CollectionChanged {1651 collection_id: eth::collection_id_to_address(collection.id),1652 }1653 .to_log(T::ContractAddress::get()),1654 );16551656 collection.save()1657 }16581659 1660 fn clamp_limits(1661 mode: CollectionMode,1662 old_limit: &CollectionLimits,1663 mut new_limit: CollectionLimits,1664 ) -> Result<CollectionLimits, DispatchError> {1665 let limits = old_limit;1666 limit_default!(old_limit, new_limit,1667 account_token_ownership_limit => ensure!(1668 new_limit <= MAX_TOKEN_OWNERSHIP,1669 <Error<T>>::CollectionLimitBoundsExceeded,1670 ),1671 sponsored_data_size => ensure!(1672 new_limit <= CUSTOM_DATA_LIMIT,1673 <Error<T>>::CollectionLimitBoundsExceeded,1674 ),16751676 sponsored_data_rate_limit => {},1677 token_limit => ensure!(1678 old_limit >= new_limit && new_limit > 0,1679 <Error<T>>::CollectionTokenLimitExceeded1680 ),16811682 sponsor_transfer_timeout(match mode {1683 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1684 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1685 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1686 }) => ensure!(1687 new_limit <= MAX_SPONSOR_TIMEOUT,1688 <Error<T>>::CollectionLimitBoundsExceeded,1689 ),1690 sponsor_approve_timeout => {},1691 owner_can_transfer => ensure!(1692 !limits.owner_can_transfer_instaled() ||1693 old_limit || !new_limit,1694 <Error<T>>::OwnerPermissionsCantBeReverted,1695 ),1696 owner_can_destroy => ensure!(1697 old_limit || !new_limit,1698 <Error<T>>::OwnerPermissionsCantBeReverted,1699 ),1700 transfers_enabled => {},1701 );1702 Ok(new_limit)1703 }17041705 1706 pub fn update_permissions(1707 user: &T::CrossAccountId,1708 collection: &mut CollectionHandle<T>,1709 new_permission: CollectionPermissions,1710 ) -> DispatchResult {1711 collection.check_is_internal()?;1712 collection.check_is_owner_or_admin(user)?;1713 collection.permissions = Self::clamp_permissions(1714 collection.mode.clone(),1715 &collection.permissions,1716 new_permission,1717 )?;17181719 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1720 <PalletEvm<T>>::deposit_log(1721 erc::CollectionHelpersEvents::CollectionChanged {1722 collection_id: eth::collection_id_to_address(collection.id),1723 }1724 .to_log(T::ContractAddress::get()),1725 );17261727 collection.save()1728 }17291730 1731 fn clamp_permissions(1732 _mode: CollectionMode,1733 old_permission: &CollectionPermissions,1734 mut new_permission: CollectionPermissions,1735 ) -> Result<CollectionPermissions, DispatchError> {1736 limit_default_clone!(old_permission, new_permission,1737 access => {},1738 mint_mode => {},1739 nesting => { },1740 );1741 Ok(new_permission)1742 }17431744 1745 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1746 CollectionProperties::<T>::mutate(collection_id, |properties| {1747 properties.recompute_consumed_space();1748 });17491750 Ok(())1751 }1752}175317541755#[macro_export]1756macro_rules! unsupported {1757 ($runtime:path) => {1758 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1759 };1760}176117621763pub trait CommonWeightInfo<CrossAccountId> {1764 1765 fn create_item() -> Weight;17661767 1768 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17691770 1771 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17721773 1774 fn burn_item() -> Weight;17751776 1777 1778 1779 fn set_collection_properties(amount: u32) -> Weight;17801781 1782 1783 1784 fn delete_collection_properties(amount: u32) -> Weight;17851786 1787 1788 1789 fn set_token_properties(amount: u32) -> Weight;17901791 1792 1793 1794 fn delete_token_properties(amount: u32) -> Weight;17951796 1797 1798 1799 fn set_token_property_permissions(amount: u32) -> Weight;18001801 1802 fn transfer() -> Weight;18031804 1805 fn approve() -> Weight;18061807 1808 fn approve_from() -> Weight;18091810 1811 fn transfer_from() -> Weight;18121813 1814 fn burn_from() -> Weight;18151816 1817 1818 1819 1820 fn burn_recursively_self_raw() -> Weight;18211822 1823 1824 1825 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18261827 1828 1829 1830 1831 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1832 Self::burn_recursively_self_raw()1833 .saturating_mul(max_selfs.max(1) as u64)1834 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1835 }18361837 1838 fn token_owner() -> Weight;18391840 1841 fn set_allowance_for_all() -> Weight;18421843 1844 fn force_repair_item() -> Weight;1845}184618471848pub trait RefungibleExtensionsWeightInfo {1849 1850 fn repartition() -> Weight;1851}185218531854185518561857pub trait CommonCollectionOperations<T: Config> {1858 1859 1860 1861 1862 1863 1864 fn create_item(1865 &self,1866 sender: T::CrossAccountId,1867 to: T::CrossAccountId,1868 data: CreateItemData,1869 nesting_budget: &dyn Budget,1870 ) -> DispatchResultWithPostInfo;18711872 1873 1874 1875 1876 1877 1878 fn create_multiple_items(1879 &self,1880 sender: T::CrossAccountId,1881 to: T::CrossAccountId,1882 data: Vec<CreateItemData>,1883 nesting_budget: &dyn Budget,1884 ) -> DispatchResultWithPostInfo;18851886 1887 1888 1889 1890 1891 1892 fn create_multiple_items_ex(1893 &self,1894 sender: T::CrossAccountId,1895 data: CreateItemExData<T::CrossAccountId>,1896 nesting_budget: &dyn Budget,1897 ) -> DispatchResultWithPostInfo;18981899 1900 1901 1902 1903 1904 fn burn_item(1905 &self,1906 sender: T::CrossAccountId,1907 token: TokenId,1908 amount: u128,1909 ) -> DispatchResultWithPostInfo;19101911 1912 1913 1914 1915 1916 1917 fn burn_item_recursively(1918 &self,1919 sender: T::CrossAccountId,1920 token: TokenId,1921 self_budget: &dyn Budget,1922 breadth_budget: &dyn Budget,1923 ) -> DispatchResultWithPostInfo;19241925 1926 1927 1928 1929 fn set_collection_properties(1930 &self,1931 sender: T::CrossAccountId,1932 properties: Vec<Property>,1933 ) -> DispatchResultWithPostInfo;19341935 1936 1937 1938 1939 fn delete_collection_properties(1940 &self,1941 sender: &T::CrossAccountId,1942 property_keys: Vec<PropertyKey>,1943 ) -> DispatchResultWithPostInfo;19441945 1946 1947 1948 1949 1950 1951 1952 1953 1954 fn set_token_properties(1955 &self,1956 sender: T::CrossAccountId,1957 token_id: TokenId,1958 properties: Vec<Property>,1959 budget: &dyn Budget,1960 ) -> DispatchResultWithPostInfo;19611962 1963 1964 1965 1966 1967 1968 1969 1970 1971 fn delete_token_properties(1972 &self,1973 sender: T::CrossAccountId,1974 token_id: TokenId,1975 property_keys: Vec<PropertyKey>,1976 budget: &dyn Budget,1977 ) -> DispatchResultWithPostInfo;19781979 1980 1981 1982 1983 1984 1985 fn set_token_property_permissions(1986 &self,1987 sender: &T::CrossAccountId,1988 property_permissions: Vec<PropertyKeyPermission>,1989 ) -> DispatchResultWithPostInfo;19901991 1992 1993 1994 1995 1996 1997 1998 fn transfer(1999 &self,2000 sender: T::CrossAccountId,2001 to: T::CrossAccountId,2002 token: TokenId,2003 amount: u128,2004 budget: &dyn Budget,2005 ) -> DispatchResultWithPostInfo;20062007 2008 2009 2010 2011 2012 2013 fn approve(2014 &self,2015 sender: T::CrossAccountId,2016 spender: T::CrossAccountId,2017 token: TokenId,2018 amount: u128,2019 ) -> DispatchResultWithPostInfo;20202021 2022 2023 2024 2025 2026 2027 2028 fn approve_from(2029 &self,2030 sender: T::CrossAccountId,2031 from: T::CrossAccountId,2032 to: T::CrossAccountId,2033 token: TokenId,2034 amount: u128,2035 ) -> DispatchResultWithPostInfo;20362037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 fn transfer_from(2048 &self,2049 sender: T::CrossAccountId,2050 from: T::CrossAccountId,2051 to: T::CrossAccountId,2052 token: TokenId,2053 amount: u128,2054 budget: &dyn Budget,2055 ) -> DispatchResultWithPostInfo;20562057 2058 2059 2060 2061 2062 2063 2064 2065 2066 fn burn_from(2067 &self,2068 sender: T::CrossAccountId,2069 from: T::CrossAccountId,2070 token: TokenId,2071 amount: u128,2072 budget: &dyn Budget,2073 ) -> DispatchResultWithPostInfo;20742075 2076 2077 2078 2079 2080 2081 fn check_nesting(2082 &self,2083 sender: T::CrossAccountId,2084 from: (CollectionId, TokenId),2085 under: TokenId,2086 budget: &dyn Budget,2087 ) -> DispatchResult;20882089 2090 2091 2092 2093 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20942095 2096 2097 2098 2099 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21002101 2102 2103 2104 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21052106 2107 fn collection_tokens(&self) -> Vec<TokenId>;21082109 2110 2111 2112 fn token_exists(&self, token: TokenId) -> bool;21132114 2115 fn last_token_id(&self) -> TokenId;21162117 2118 2119 2120 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;21212122 2123 2124 2125 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;21262127 2128 2129 2130 2131 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21322133 2134 2135 2136 2137 2138 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21392140 2141 fn total_supply(&self) -> u32;21422143 2144 2145 2146 fn account_balance(&self, account: T::CrossAccountId) -> u32;21472148 2149 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21502151 2152 fn total_pieces(&self, token: TokenId) -> Option<u128>;21532154 2155 2156 2157 2158 2159 fn allowance(2160 &self,2161 sender: T::CrossAccountId,2162 spender: T::CrossAccountId,2163 token: TokenId,2164 ) -> u128;21652166 2167 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21682169 2170 2171 2172 2173 fn set_allowance_for_all(2174 &self,2175 owner: T::CrossAccountId,2176 operator: T::CrossAccountId,2177 approve: bool,2178 ) -> DispatchResultWithPostInfo;21792180 2181 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21822183 2184 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2185}218621872188pub trait RefungibleExtensions<T>2189where2190 T: Config,2191{2192 2193 2194 2195 2196 2197 2198 2199 fn repartition(2200 &self,2201 sender: &T::CrossAccountId,2202 token: TokenId,2203 amount: u128,2204 ) -> DispatchResultWithPostInfo;2205}22062207220822092210pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2211 let post_info = PostDispatchInfo {2212 actual_weight: Some(weight),2213 pays_fee: Pays::Yes,2214 };2215 match res {2216 Ok(()) => Ok(post_info),2217 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2218 }2219}22202221impl<T: Config> From<PropertiesError> for Error<T> {2222 fn from(error: PropertiesError) -> Self {2223 match error {2224 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2225 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2226 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2227 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2228 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2229 }2230 }2231}