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 1202 1203 1204 1205 1206 1207 1208 1209 #[transactional]1210 fn modify_collection_properties(1211 collection: &CollectionHandle<T>,1212 sender: &T::CrossAccountId,1213 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1214 ) -> DispatchResult {1215 collection.check_is_owner_or_admin(sender)?;12161217 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12181219 for (key, value) in properties_updates {1220 match value {1221 Some(value) => {1222 stored_properties1223 .try_set(key.clone(), value)1224 .map_err(<Error<T>>::from)?;12251226 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1227 <PalletEvm<T>>::deposit_log(1228 erc::CollectionHelpersEvents::CollectionChanged {1229 collection_id: eth::collection_id_to_address(collection.id),1230 }1231 .to_log(T::ContractAddress::get()),1232 );1233 }1234 None => {1235 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12361237 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1238 <PalletEvm<T>>::deposit_log(1239 erc::CollectionHelpersEvents::CollectionChanged {1240 collection_id: eth::collection_id_to_address(collection.id),1241 }1242 .to_log(T::ContractAddress::get()),1243 );1244 }1245 }1246 }12471248 <CollectionProperties<T>>::set(collection.id, stored_properties);12491250 Ok(())1251 }12521253 1254 1255 1256 1257 1258 pub fn set_collection_property(1259 collection: &CollectionHandle<T>,1260 sender: &T::CrossAccountId,1261 property: Property,1262 ) -> DispatchResult {1263 Self::set_collection_properties(collection, sender, [property].into_iter())1264 }12651266 1267 1268 1269 1270 1271 1272 pub fn set_scoped_collection_property(1273 collection_id: CollectionId,1274 scope: PropertyScope,1275 property: Property,1276 ) -> DispatchResult {1277 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1278 properties.try_scoped_set(scope, property.key, property.value)1279 })1280 .map_err(<Error<T>>::from)?;12811282 Ok(())1283 }12841285 1286 1287 1288 1289 1290 1291 pub fn set_scoped_collection_properties(1292 collection_id: CollectionId,1293 scope: PropertyScope,1294 properties: impl Iterator<Item = Property>,1295 ) -> DispatchResult {1296 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1297 stored_properties.try_scoped_set_from_iter(scope, properties)1298 })1299 .map_err(<Error<T>>::from)?;13001301 Ok(())1302 }13031304 1305 1306 1307 1308 1309 pub fn set_collection_properties(1310 collection: &CollectionHandle<T>,1311 sender: &T::CrossAccountId,1312 properties: impl Iterator<Item = Property>,1313 ) -> DispatchResult {1314 Self::modify_collection_properties(1315 collection,1316 sender,1317 properties.map(|property| (property.key, Some(property.value))),1318 )1319 }13201321 1322 1323 1324 1325 1326 pub fn delete_collection_property(1327 collection: &CollectionHandle<T>,1328 sender: &T::CrossAccountId,1329 property_key: PropertyKey,1330 ) -> DispatchResult {1331 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1332 }13331334 1335 1336 1337 1338 1339 pub fn delete_collection_properties(1340 collection: &CollectionHandle<T>,1341 sender: &T::CrossAccountId,1342 property_keys: impl Iterator<Item = PropertyKey>,1343 ) -> DispatchResult {1344 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1345 }13461347 1348 1349 1350 1351 1352 1353 pub fn set_property_permission_unchecked(1354 collection: CollectionId,1355 property_permission: PropertyKeyPermission,1356 ) -> DispatchResult {1357 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1358 permissions.try_set(property_permission.key, property_permission.permission)1359 })1360 .map_err(<Error<T>>::from)?;1361 Ok(())1362 }13631364 1365 1366 1367 1368 1369 pub fn set_property_permission(1370 collection: &CollectionHandle<T>,1371 sender: &T::CrossAccountId,1372 property_permission: PropertyKeyPermission,1373 ) -> DispatchResult {1374 Self::set_scoped_property_permission(1375 collection,1376 sender,1377 PropertyScope::None,1378 property_permission,1379 )1380 }13811382 1383 1384 1385 1386 1387 1388 pub fn set_scoped_property_permission(1389 collection: &CollectionHandle<T>,1390 sender: &T::CrossAccountId,1391 scope: PropertyScope,1392 property_permission: PropertyKeyPermission,1393 ) -> DispatchResult {1394 collection.check_is_owner_or_admin(sender)?;13951396 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1397 let current_permission = all_permissions.get(&property_permission.key);1398 if matches![1399 current_permission,1400 Some(PropertyPermission { mutable: false, .. })1401 ] {1402 return Err(<Error<T>>::NoPermission.into());1403 }14041405 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1406 let property_permission = property_permission.clone();1407 permissions.try_scoped_set(1408 scope,1409 property_permission.key,1410 property_permission.permission,1411 )1412 })1413 .map_err(<Error<T>>::from)?;14141415 Self::deposit_event(Event::PropertyPermissionSet(1416 collection.id,1417 property_permission.key,1418 ));1419 <PalletEvm<T>>::deposit_log(1420 erc::CollectionHelpersEvents::CollectionChanged {1421 collection_id: eth::collection_id_to_address(collection.id),1422 }1423 .to_log(T::ContractAddress::get()),1424 );14251426 Ok(())1427 }14281429 1430 1431 1432 1433 1434 #[transactional]1435 pub fn set_token_property_permissions(1436 collection: &CollectionHandle<T>,1437 sender: &T::CrossAccountId,1438 property_permissions: Vec<PropertyKeyPermission>,1439 ) -> DispatchResult {1440 Self::set_scoped_token_property_permissions(1441 collection,1442 sender,1443 PropertyScope::None,1444 property_permissions,1445 )1446 }14471448 1449 1450 1451 1452 1453 1454 #[transactional]1455 pub fn set_scoped_token_property_permissions(1456 collection: &CollectionHandle<T>,1457 sender: &T::CrossAccountId,1458 scope: PropertyScope,1459 property_permissions: Vec<PropertyKeyPermission>,1460 ) -> DispatchResult {1461 for prop_pemission in property_permissions {1462 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1463 }14641465 Ok(())1466 }14671468 1469 pub fn get_collection_property(1470 collection_id: CollectionId,1471 key: &PropertyKey,1472 ) -> Option<PropertyValue> {1473 Self::collection_properties(collection_id).get(key).cloned()1474 }14751476 1477 pub fn bytes_keys_to_property_keys(1478 keys: Vec<Vec<u8>>,1479 ) -> Result<Vec<PropertyKey>, DispatchError> {1480 keys.into_iter()1481 .map(|key| -> Result<PropertyKey, DispatchError> {1482 key.try_into()1483 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1484 })1485 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1486 }14871488 1489 pub fn filter_collection_properties(1490 collection_id: CollectionId,1491 keys: Option<Vec<PropertyKey>>,1492 ) -> Result<Vec<Property>, DispatchError> {1493 let properties = Self::collection_properties(collection_id);14941495 let properties = keys1496 .map(|keys| {1497 keys.into_iter()1498 .filter_map(|key| {1499 properties.get(&key).map(|value| Property {1500 key,1501 value: value.clone(),1502 })1503 })1504 .collect()1505 })1506 .unwrap_or_else(|| {1507 properties1508 .into_iter()1509 .map(|(key, value)| Property { key, value })1510 .collect()1511 });15121513 Ok(properties)1514 }15151516 1517 pub fn filter_property_permissions(1518 collection_id: CollectionId,1519 keys: Option<Vec<PropertyKey>>,1520 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1521 let permissions = Self::property_permissions(collection_id);15221523 let key_permissions = keys1524 .map(|keys| {1525 keys.into_iter()1526 .filter_map(|key| {1527 permissions1528 .get(&key)1529 .map(|permission| PropertyKeyPermission {1530 key,1531 permission: permission.clone(),1532 })1533 })1534 .collect()1535 })1536 .unwrap_or_else(|| {1537 permissions1538 .into_iter()1539 .map(|(key, permission)| PropertyKeyPermission { key, permission })1540 .collect()1541 });15421543 Ok(key_permissions)1544 }15451546 1547 1548 1549 pub fn toggle_allowlist(1550 collection: &CollectionHandle<T>,1551 sender: &T::CrossAccountId,1552 user: &T::CrossAccountId,1553 allowed: bool,1554 ) -> DispatchResult {1555 collection.check_is_owner_or_admin(sender)?;15561557 15581559 if allowed {1560 <Allowlist<T>>::insert((collection.id, user), true);1561 Self::deposit_event(Event::<T>::AllowListAddressAdded(1562 collection.id,1563 user.clone(),1564 ));1565 } else {1566 <Allowlist<T>>::remove((collection.id, user));1567 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1568 collection.id,1569 user.clone(),1570 ));1571 }15721573 <PalletEvm<T>>::deposit_log(1574 erc::CollectionHelpersEvents::CollectionChanged {1575 collection_id: eth::collection_id_to_address(collection.id),1576 }1577 .to_log(T::ContractAddress::get()),1578 );15791580 Ok(())1581 }15821583 1584 1585 1586 pub fn toggle_admin(1587 collection: &CollectionHandle<T>,1588 sender: &T::CrossAccountId,1589 user: &T::CrossAccountId,1590 admin: bool,1591 ) -> DispatchResult {1592 collection.check_is_internal()?;1593 collection.check_is_owner(sender)?;15941595 let is_admin = <IsAdmin<T>>::get((collection.id, user));1596 if is_admin == admin {1597 if admin {1598 return Ok(());1599 } else {1600 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1601 }1602 }1603 let amount = <AdminAmount<T>>::get(collection.id);16041605 16061607 if admin {1608 let amount = amount1609 .checked_add(1)1610 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1611 ensure!(1612 amount <= Self::collection_admins_limit(),1613 <Error<T>>::CollectionAdminCountExceeded,1614 );16151616 <AdminAmount<T>>::insert(collection.id, amount);1617 <IsAdmin<T>>::insert((collection.id, user), true);16181619 Self::deposit_event(Event::<T>::CollectionAdminAdded(1620 collection.id,1621 user.clone(),1622 ));1623 } else {1624 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1625 <IsAdmin<T>>::remove((collection.id, user));16261627 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1628 collection.id,1629 user.clone(),1630 ));1631 }16321633 <PalletEvm<T>>::deposit_log(1634 erc::CollectionHelpersEvents::CollectionChanged {1635 collection_id: eth::collection_id_to_address(collection.id),1636 }1637 .to_log(T::ContractAddress::get()),1638 );16391640 Ok(())1641 }16421643 1644 pub fn update_limits(1645 user: &T::CrossAccountId,1646 collection: &mut CollectionHandle<T>,1647 new_limit: CollectionLimits,1648 ) -> DispatchResult {1649 collection.check_is_internal()?;1650 collection.check_is_owner_or_admin(user)?;16511652 collection.limits =1653 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16541655 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1656 <PalletEvm<T>>::deposit_log(1657 erc::CollectionHelpersEvents::CollectionChanged {1658 collection_id: eth::collection_id_to_address(collection.id),1659 }1660 .to_log(T::ContractAddress::get()),1661 );16621663 collection.save()1664 }16651666 1667 fn clamp_limits(1668 mode: CollectionMode,1669 old_limit: &CollectionLimits,1670 mut new_limit: CollectionLimits,1671 ) -> Result<CollectionLimits, DispatchError> {1672 let limits = old_limit;1673 limit_default!(old_limit, new_limit,1674 account_token_ownership_limit => ensure!(1675 new_limit <= MAX_TOKEN_OWNERSHIP,1676 <Error<T>>::CollectionLimitBoundsExceeded,1677 ),1678 sponsored_data_size => ensure!(1679 new_limit <= CUSTOM_DATA_LIMIT,1680 <Error<T>>::CollectionLimitBoundsExceeded,1681 ),16821683 sponsored_data_rate_limit => {},1684 token_limit => ensure!(1685 old_limit >= new_limit && new_limit > 0,1686 <Error<T>>::CollectionTokenLimitExceeded1687 ),16881689 sponsor_transfer_timeout(match mode {1690 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1691 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1692 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1693 }) => ensure!(1694 new_limit <= MAX_SPONSOR_TIMEOUT,1695 <Error<T>>::CollectionLimitBoundsExceeded,1696 ),1697 sponsor_approve_timeout => {},1698 owner_can_transfer => ensure!(1699 !limits.owner_can_transfer_instaled() ||1700 old_limit || !new_limit,1701 <Error<T>>::OwnerPermissionsCantBeReverted,1702 ),1703 owner_can_destroy => ensure!(1704 old_limit || !new_limit,1705 <Error<T>>::OwnerPermissionsCantBeReverted,1706 ),1707 transfers_enabled => {},1708 );1709 Ok(new_limit)1710 }17111712 1713 pub fn update_permissions(1714 user: &T::CrossAccountId,1715 collection: &mut CollectionHandle<T>,1716 new_permission: CollectionPermissions,1717 ) -> DispatchResult {1718 collection.check_is_internal()?;1719 collection.check_is_owner_or_admin(user)?;1720 collection.permissions = Self::clamp_permissions(1721 collection.mode.clone(),1722 &collection.permissions,1723 new_permission,1724 )?;17251726 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1727 <PalletEvm<T>>::deposit_log(1728 erc::CollectionHelpersEvents::CollectionChanged {1729 collection_id: eth::collection_id_to_address(collection.id),1730 }1731 .to_log(T::ContractAddress::get()),1732 );17331734 collection.save()1735 }17361737 1738 fn clamp_permissions(1739 _mode: CollectionMode,1740 old_permission: &CollectionPermissions,1741 mut new_permission: CollectionPermissions,1742 ) -> Result<CollectionPermissions, DispatchError> {1743 limit_default_clone!(old_permission, new_permission,1744 access => {},1745 mint_mode => {},1746 nesting => { },1747 );1748 Ok(new_permission)1749 }17501751 1752 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1753 CollectionProperties::<T>::mutate(collection_id, |properties| {1754 properties.recompute_consumed_space();1755 });17561757 Ok(())1758 }1759}176017611762#[macro_export]1763macro_rules! unsupported {1764 ($runtime:path) => {1765 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1766 };1767}176817691770pub trait CommonWeightInfo<CrossAccountId> {1771 1772 fn create_item() -> Weight;17731774 1775 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17761777 1778 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17791780 1781 fn burn_item() -> Weight;17821783 1784 1785 1786 fn set_collection_properties(amount: u32) -> Weight;17871788 1789 1790 1791 fn delete_collection_properties(amount: u32) -> Weight;17921793 1794 1795 1796 fn set_token_properties(amount: u32) -> Weight;17971798 1799 1800 1801 fn delete_token_properties(amount: u32) -> Weight;18021803 1804 1805 1806 fn set_token_property_permissions(amount: u32) -> Weight;18071808 1809 fn transfer() -> Weight;18101811 1812 fn approve() -> Weight;18131814 1815 fn approve_from() -> Weight;18161817 1818 fn transfer_from() -> Weight;18191820 1821 fn burn_from() -> Weight;18221823 1824 1825 1826 1827 fn burn_recursively_self_raw() -> Weight;18281829 1830 1831 1832 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18331834 1835 1836 1837 1838 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1839 Self::burn_recursively_self_raw()1840 .saturating_mul(max_selfs.max(1) as u64)1841 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1842 }18431844 1845 fn token_owner() -> Weight;18461847 1848 fn set_allowance_for_all() -> Weight;18491850 1851 fn force_repair_item() -> Weight;1852}185318541855pub trait RefungibleExtensionsWeightInfo {1856 1857 fn repartition() -> Weight;1858}185918601861186218631864pub trait CommonCollectionOperations<T: Config> {1865 1866 1867 1868 1869 1870 1871 fn create_item(1872 &self,1873 sender: T::CrossAccountId,1874 to: T::CrossAccountId,1875 data: CreateItemData,1876 nesting_budget: &dyn Budget,1877 ) -> DispatchResultWithPostInfo;18781879 1880 1881 1882 1883 1884 1885 fn create_multiple_items(1886 &self,1887 sender: T::CrossAccountId,1888 to: T::CrossAccountId,1889 data: Vec<CreateItemData>,1890 nesting_budget: &dyn Budget,1891 ) -> DispatchResultWithPostInfo;18921893 1894 1895 1896 1897 1898 1899 fn create_multiple_items_ex(1900 &self,1901 sender: T::CrossAccountId,1902 data: CreateItemExData<T::CrossAccountId>,1903 nesting_budget: &dyn Budget,1904 ) -> DispatchResultWithPostInfo;19051906 1907 1908 1909 1910 1911 fn burn_item(1912 &self,1913 sender: T::CrossAccountId,1914 token: TokenId,1915 amount: u128,1916 ) -> DispatchResultWithPostInfo;19171918 1919 1920 1921 1922 1923 1924 fn burn_item_recursively(1925 &self,1926 sender: T::CrossAccountId,1927 token: TokenId,1928 self_budget: &dyn Budget,1929 breadth_budget: &dyn Budget,1930 ) -> DispatchResultWithPostInfo;19311932 1933 1934 1935 1936 fn set_collection_properties(1937 &self,1938 sender: T::CrossAccountId,1939 properties: Vec<Property>,1940 ) -> DispatchResultWithPostInfo;19411942 1943 1944 1945 1946 fn delete_collection_properties(1947 &self,1948 sender: &T::CrossAccountId,1949 property_keys: Vec<PropertyKey>,1950 ) -> DispatchResultWithPostInfo;19511952 1953 1954 1955 1956 1957 1958 1959 1960 1961 fn set_token_properties(1962 &self,1963 sender: T::CrossAccountId,1964 token_id: TokenId,1965 properties: Vec<Property>,1966 budget: &dyn Budget,1967 ) -> DispatchResultWithPostInfo;19681969 1970 1971 1972 1973 1974 1975 1976 1977 1978 fn delete_token_properties(1979 &self,1980 sender: T::CrossAccountId,1981 token_id: TokenId,1982 property_keys: Vec<PropertyKey>,1983 budget: &dyn Budget,1984 ) -> DispatchResultWithPostInfo;19851986 1987 1988 1989 1990 1991 1992 fn set_token_property_permissions(1993 &self,1994 sender: &T::CrossAccountId,1995 property_permissions: Vec<PropertyKeyPermission>,1996 ) -> DispatchResultWithPostInfo;19971998 1999 2000 2001 2002 2003 2004 2005 fn transfer(2006 &self,2007 sender: T::CrossAccountId,2008 to: T::CrossAccountId,2009 token: TokenId,2010 amount: u128,2011 budget: &dyn Budget,2012 ) -> DispatchResultWithPostInfo;20132014 2015 2016 2017 2018 2019 2020 fn approve(2021 &self,2022 sender: T::CrossAccountId,2023 spender: T::CrossAccountId,2024 token: TokenId,2025 amount: u128,2026 ) -> DispatchResultWithPostInfo;20272028 2029 2030 2031 2032 2033 2034 2035 fn approve_from(2036 &self,2037 sender: T::CrossAccountId,2038 from: T::CrossAccountId,2039 to: T::CrossAccountId,2040 token: TokenId,2041 amount: u128,2042 ) -> DispatchResultWithPostInfo;20432044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 fn transfer_from(2055 &self,2056 sender: T::CrossAccountId,2057 from: T::CrossAccountId,2058 to: T::CrossAccountId,2059 token: TokenId,2060 amount: u128,2061 budget: &dyn Budget,2062 ) -> DispatchResultWithPostInfo;20632064 2065 2066 2067 2068 2069 2070 2071 2072 2073 fn burn_from(2074 &self,2075 sender: T::CrossAccountId,2076 from: T::CrossAccountId,2077 token: TokenId,2078 amount: u128,2079 budget: &dyn Budget,2080 ) -> DispatchResultWithPostInfo;20812082 2083 2084 2085 2086 2087 2088 fn check_nesting(2089 &self,2090 sender: T::CrossAccountId,2091 from: (CollectionId, TokenId),2092 under: TokenId,2093 budget: &dyn Budget,2094 ) -> DispatchResult;20952096 2097 2098 2099 2100 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21012102 2103 2104 2105 2106 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21072108 2109 2110 2111 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21122113 2114 fn collection_tokens(&self) -> Vec<TokenId>;21152116 2117 2118 2119 fn token_exists(&self, token: TokenId) -> bool;21202121 2122 fn last_token_id(&self) -> TokenId;21232124 2125 2126 2127 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;21282129 2130 2131 2132 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;21332134 2135 2136 2137 2138 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21392140 2141 2142 2143 2144 2145 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21462147 2148 fn total_supply(&self) -> u32;21492150 2151 2152 2153 fn account_balance(&self, account: T::CrossAccountId) -> u32;21542155 2156 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21572158 2159 fn total_pieces(&self, token: TokenId) -> Option<u128>;21602161 2162 2163 2164 2165 2166 fn allowance(2167 &self,2168 sender: T::CrossAccountId,2169 spender: T::CrossAccountId,2170 token: TokenId,2171 ) -> u128;21722173 2174 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21752176 2177 2178 2179 2180 fn set_allowance_for_all(2181 &self,2182 owner: T::CrossAccountId,2183 operator: T::CrossAccountId,2184 approve: bool,2185 ) -> DispatchResultWithPostInfo;21862187 2188 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21892190 2191 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2192}219321942195pub trait RefungibleExtensions<T>2196where2197 T: Config,2198{2199 2200 2201 2202 2203 2204 2205 2206 fn repartition(2207 &self,2208 sender: &T::CrossAccountId,2209 token: TokenId,2210 amount: u128,2211 ) -> DispatchResultWithPostInfo;2212}22132214221522162217pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2218 let post_info = PostDispatchInfo {2219 actual_weight: Some(weight),2220 pays_fee: Pays::Yes,2221 };2222 match res {2223 Ok(()) => Ok(post_info),2224 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2225 }2226}22272228impl<T: Config> From<PropertiesError> for Error<T> {2229 fn from(error: PropertiesError) -> Self {2230 match error {2231 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2232 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2233 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2234 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2235 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2236 }2237 }2238}