12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#![warn(missing_docs)]53#![cfg_attr(not(feature = "std"), no_std)]54extern crate alloc;5556use core::ops::{Deref, DerefMut};57use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};58use sp_std::vec::Vec;59use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};60use evm_coder::ToLog;61use frame_support::{62 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},63 ensure,64 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},65 dispatch::Pays,66 transactional,67};68use pallet_evm::GasWeightMapping;69use up_data_structs::{70 COLLECTION_NUMBER_LIMIT,71 Collection,72 RpcCollection,73 CollectionFlags,74 RpcCollectionFlags,75 CollectionId,76 CreateItemData,77 MAX_TOKEN_PREFIX_LENGTH,78 COLLECTION_ADMINS_LIMIT,79 TokenId,80 TokenChild,81 CollectionStats,82 MAX_TOKEN_OWNERSHIP,83 CollectionMode,84 NFT_SPONSOR_TRANSFER_TIMEOUT,85 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,86 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,87 MAX_SPONSOR_TIMEOUT,88 CUSTOM_DATA_LIMIT,89 CollectionLimits,90 CreateCollectionData,91 SponsorshipState,92 CreateItemExData,93 SponsoringRateLimit,94 budget::Budget,95 PhantomType,96 Property,97 Properties,98 PropertiesPermissionMap,99 PropertyKey,100 PropertyValue,101 PropertyPermission,102 PropertiesError,103 PropertyKeyPermission,104 TokenData,105 TrySetProperty,106 PropertyScope,107 108 RmrkCollectionInfo,109 RmrkInstanceInfo,110 RmrkResourceInfo,111 RmrkPropertyInfo,112 RmrkBaseInfo,113 RmrkPartType,114 RmrkBoundedTheme,115 RmrkNftChild,116 CollectionPermissions,117};118use up_pov_estimate_rpc::PovInfo;119120pub use pallet::*;121use sp_core::H160;122use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};123#[cfg(feature = "runtime-benchmarks")]124pub mod benchmarking;125pub mod dispatch;126pub mod erc;127pub mod eth;128pub mod weights;129130131pub type SelfWeightOf<T> = <T as Config>::WeightInfo;132133134135136137138139#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]140pub struct CollectionHandle<T: Config> {141 142 pub id: CollectionId,143 collection: Collection<T::AccountId>,144 145 pub recorder: SubstrateRecorder<T>,146}147148impl<T: Config> WithRecorder<T> for CollectionHandle<T> {149 fn recorder(&self) -> &SubstrateRecorder<T> {150 &self.recorder151 }152 fn into_recorder(self) -> SubstrateRecorder<T> {153 self.recorder154 }155}156157impl<T: Config> CollectionHandle<T> {158 159 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {160 <CollectionById<T>>::get(id).map(|collection| Self {161 id,162 collection,163 recorder: SubstrateRecorder::new(gas_limit),164 })165 }166167 168 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {169 <CollectionById<T>>::get(id).map(|collection| Self {170 id,171 collection,172 recorder,173 })174 }175176 177 178 pub fn new(id: CollectionId) -> Option<Self> {179 Self::new_with_gas_limit(id, u64::MAX)180 }181182 183 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {184 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)185 }186187 188 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {189 self.recorder190 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(191 <T as frame_system::Config>::DbWeight::get()192 .read193 .saturating_mul(reads),194 )))195 }196197 198 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {199 self.recorder200 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(201 <T as frame_system::Config>::DbWeight::get()202 .write203 .saturating_mul(writes),204 )))205 }206207 208 pub fn consume_store_reads_and_writes(209 &self,210 reads: u64,211 writes: u64,212 ) -> evm_coder::execution::Result<()> {213 let weight = <T as frame_system::Config>::DbWeight::get();214 let reads = weight.read.saturating_mul(reads);215 let writes = weight.read.saturating_mul(writes);216 self.recorder217 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(218 reads.saturating_add(writes),219 )))220 }221222 223 pub fn save(&self) -> DispatchResult {224 <CollectionById<T>>::insert(self.id, &self.collection);225 Ok(())226 }227228 229 230 231 232 233 pub fn set_sponsor(234 &mut self,235 sender: &T::CrossAccountId,236 sponsor: T::AccountId,237 ) -> DispatchResult {238 self.check_is_internal()?;239 self.check_is_owner_or_admin(sender)?;240241 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());242243 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));244 <PalletEvm<T>>::deposit_log(245 erc::CollectionHelpersEvents::CollectionChanged {246 collection_id: eth::collection_id_to_address(self.id),247 }248 .to_log(T::ContractAddress::get()),249 );250251 self.save()252 }253254 255 256 257 258 259 260 261 262 263 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {264 self.check_is_internal()?;265266 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());267268 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));269 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));270 <PalletEvm<T>>::deposit_log(271 erc::CollectionHelpersEvents::CollectionChanged {272 collection_id: eth::collection_id_to_address(self.id),273 }274 .to_log(T::ContractAddress::get()),275 );276277 self.save()278 }279280 281 282 283 284 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {285 self.check_is_internal()?;286 ensure!(287 self.collection.sponsorship.pending_sponsor() == Some(sender),288 Error::<T>::ConfirmSponsorshipFail289 );290291 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());292293 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));294 <PalletEvm<T>>::deposit_log(295 erc::CollectionHelpersEvents::CollectionChanged {296 collection_id: eth::collection_id_to_address(self.id),297 }298 .to_log(T::ContractAddress::get()),299 );300301 self.save()302 }303304 305 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {306 self.check_is_internal()?;307 self.check_is_owner_or_admin(sender)?;308309 self.collection.sponsorship = SponsorshipState::Disabled;310311 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));312 <PalletEvm<T>>::deposit_log(313 erc::CollectionHelpersEvents::CollectionChanged {314 collection_id: eth::collection_id_to_address(self.id),315 }316 .to_log(T::ContractAddress::get()),317 );318 self.save()319 }320321 322 323 324 325 pub fn force_remove_sponsor(&mut self) -> DispatchResult {326 self.check_is_internal()?;327328 self.collection.sponsorship = SponsorshipState::Disabled;329330 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));331 <PalletEvm<T>>::deposit_log(332 erc::CollectionHelpersEvents::CollectionChanged {333 collection_id: eth::collection_id_to_address(self.id),334 }335 .to_log(T::ContractAddress::get()),336 );337 self.save()338 }339340 341 342 pub fn check_is_internal(&self) -> DispatchResult {343 if self.flags.external {344 return Err(<Error<T>>::CollectionIsExternal)?;345 }346347 Ok(())348 }349350 351 352 pub fn check_is_external(&self) -> DispatchResult {353 if !self.flags.external {354 return Err(<Error<T>>::CollectionIsInternal)?;355 }356357 Ok(())358 }359}360361impl<T: Config> Deref for CollectionHandle<T> {362 type Target = Collection<T::AccountId>;363364 fn deref(&self) -> &Self::Target {365 &self.collection366 }367}368369impl<T: Config> DerefMut for CollectionHandle<T> {370 fn deref_mut(&mut self) -> &mut Self::Target {371 &mut self.collection372 }373}374375impl<T: Config> CollectionHandle<T> {376 377 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {378 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);379 Ok(())380 }381382 383 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {384 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))385 }386387 388 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {389 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);390 Ok(())391 }392393 394 395 396 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {397 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)398 }399400 401 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {402 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)403 }404405 406 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {407 ensure!(408 <Allowlist<T>>::get((self.id, user)),409 <Error<T>>::AddressNotInAllowlist410 );411 Ok(())412 }413414 415 416 417 pub fn change_owner(418 &mut self,419 caller: T::CrossAccountId,420 new_owner: T::CrossAccountId,421 ) -> DispatchResult {422 self.check_is_internal()?;423 self.check_is_owner(&caller)?;424 self.collection.owner = new_owner.as_sub().clone();425426 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(427 self.id,428 new_owner.as_sub().clone(),429 ));430 <PalletEvm<T>>::deposit_log(431 erc::CollectionHelpersEvents::CollectionChanged {432 collection_id: eth::collection_id_to_address(self.id),433 }434 .to_log(T::ContractAddress::get()),435 );436437 self.save()438 }439}440441#[frame_support::pallet]442pub mod pallet {443 use super::*;444 use dispatch::CollectionDispatch;445 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};446 use frame_system::pallet_prelude::*;447 use frame_support::traits::Currency;448 use up_data_structs::{TokenId, mapping::TokenAddressMapping};449 use scale_info::TypeInfo;450 use weights::WeightInfo;451452 #[pallet::config]453 pub trait Config:454 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo455 {456 457 type WeightInfo: WeightInfo;458459 460 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;461462 463 type Currency: Currency<Self::AccountId>;464465 466 #[pallet::constant]467 type CollectionCreationPrice: Get<468 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,469 >;470471 472 type CollectionDispatch: CollectionDispatch<Self>;473474 475 type TreasuryAccountId: Get<Self::AccountId>;476477 478 #[pallet::constant]479 type ContractAddress: Get<H160>;480481 482 type EvmTokenAddressMapping: TokenAddressMapping<H160>;483484 485 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;486 }487488 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);489490 #[pallet::pallet]491 #[pallet::storage_version(STORAGE_VERSION)]492 #[pallet::generate_store(pub(super) trait Store)]493 pub struct Pallet<T>(_);494495 #[pallet::extra_constants]496 impl<T: Config> Pallet<T> {497 498 pub fn collection_admins_limit() -> u32 {499 COLLECTION_ADMINS_LIMIT500 }501 }502503 impl<T: Config> Pallet<T> {504 505 pub fn deposit_event(event: Event<T>) {506 let event = <T as Config>::RuntimeEvent::from(event);507 let event = event.into();508 <frame_system::Pallet<T>>::deposit_event(event)509 }510 }511512 #[pallet::event]513 pub enum Event<T: Config> {514 515 CollectionCreated(516 517 CollectionId,518 519 u8,520 521 T::AccountId,522 ),523524 525 CollectionDestroyed(526 527 CollectionId,528 ),529530 531 ItemCreated(532 533 CollectionId,534 535 TokenId,536 537 T::CrossAccountId,538 539 u128,540 ),541542 543 ItemDestroyed(544 545 CollectionId,546 547 TokenId,548 549 T::CrossAccountId,550 551 u128,552 ),553554 555 Transfer(556 557 CollectionId,558 559 TokenId,560 561 T::CrossAccountId,562 563 T::CrossAccountId,564 565 u128,566 ),567568 569 Approved(570 571 CollectionId,572 573 TokenId,574 575 T::CrossAccountId,576 577 T::CrossAccountId,578 579 u128,580 ),581582 583 ApprovedForAll(584 585 CollectionId,586 587 T::CrossAccountId,588 589 T::CrossAccountId,590 591 bool,592 ),593594 595 CollectionPropertySet(596 597 CollectionId,598 599 PropertyKey,600 ),601602 603 CollectionPropertyDeleted(604 605 CollectionId,606 607 PropertyKey,608 ),609610 611 TokenPropertySet(612 613 CollectionId,614 615 TokenId,616 617 PropertyKey,618 ),619620 621 TokenPropertyDeleted(622 623 CollectionId,624 625 TokenId,626 627 PropertyKey,628 ),629630 631 PropertyPermissionSet(632 633 CollectionId,634 635 PropertyKey,636 ),637638 639 AllowListAddressAdded(640 641 CollectionId,642 643 T::CrossAccountId,644 ),645646 647 AllowListAddressRemoved(648 649 CollectionId,650 651 T::CrossAccountId,652 ),653654 655 CollectionAdminAdded(656 657 CollectionId,658 659 T::CrossAccountId,660 ),661662 663 CollectionAdminRemoved(664 665 CollectionId,666 667 T::CrossAccountId,668 ),669670 671 CollectionLimitSet(672 673 CollectionId,674 ),675676 677 CollectionOwnerChanged(678 679 CollectionId,680 681 T::AccountId,682 ),683684 685 CollectionPermissionSet(686 687 CollectionId,688 ),689690 691 CollectionSponsorSet(692 693 CollectionId,694 695 T::AccountId,696 ),697698 699 SponsorshipConfirmed(700 701 CollectionId,702 703 T::AccountId,704 ),705706 707 CollectionSponsorRemoved(708 709 CollectionId,710 ),711 }712713 #[pallet::error]714 pub enum Error<T> {715 716 CollectionNotFound,717 718 MustBeTokenOwner,719 720 NoPermission,721 722 CantDestroyNotEmptyCollection,723 724 PublicMintingNotAllowed,725 726 AddressNotInAllowlist,727728 729 CollectionNameLimitExceeded,730 731 CollectionDescriptionLimitExceeded,732 733 CollectionTokenPrefixLimitExceeded,734 735 TotalCollectionsLimitExceeded,736 737 CollectionAdminCountExceeded,738 739 CollectionLimitBoundsExceeded,740 741 OwnerPermissionsCantBeReverted,742 743 TransferNotAllowed,744 745 AccountTokenLimitExceeded,746 747 CollectionTokenLimitExceeded,748 749 MetadataFlagFrozen,750751 752 TokenNotFound,753 754 TokenValueTooLow,755 756 ApprovedValueTooLow,757 758 CantApproveMoreThanOwned,759 760 AddressIsNotEthMirror,761762 763 AddressIsZero,764765 766 UnsupportedOperation,767768 769 NotSufficientFounds,770771 772 UserIsNotAllowedToNest,773 774 SourceCollectionIsNotAllowedToNest,775776 777 CollectionFieldSizeExceeded,778779 780 NoSpaceForProperty,781782 783 PropertyLimitReached,784785 786 PropertyKeyIsTooLong,787788 789 InvalidCharacterInPropertyKey,790791 792 EmptyPropertyKey,793794 795 CollectionIsExternal,796797 798 CollectionIsInternal,799800 801 ConfirmSponsorshipFail,802803 804 UserIsNotCollectionAdmin,805 }806807 808 #[pallet::storage]809 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;810811 812 #[pallet::storage]813 pub type DestroyedCollectionCount<T> =814 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;815816 817 #[pallet::storage]818 pub type CollectionById<T> = StorageMap<819 Hasher = Blake2_128Concat,820 Key = CollectionId,821 Value = Collection<<T as frame_system::Config>::AccountId>,822 QueryKind = OptionQuery,823 >;824825 826 #[pallet::storage]827 #[pallet::getter(fn collection_properties)]828 pub type CollectionProperties<T> = StorageMap<829 Hasher = Blake2_128Concat,830 Key = CollectionId,831 Value = Properties,832 QueryKind = ValueQuery,833 OnEmpty = up_data_structs::CollectionProperties,834 >;835836 837 #[pallet::storage]838 #[pallet::getter(fn property_permissions)]839 pub type CollectionPropertyPermissions<T> = StorageMap<840 Hasher = Blake2_128Concat,841 Key = CollectionId,842 Value = PropertiesPermissionMap,843 QueryKind = ValueQuery,844 >;845846 847 #[pallet::storage]848 pub type AdminAmount<T> = StorageMap<849 Hasher = Blake2_128Concat,850 Key = CollectionId,851 Value = u32,852 QueryKind = ValueQuery,853 >;854855 856 #[pallet::storage]857 pub type IsAdmin<T: Config> = StorageNMap<858 Key = (859 Key<Blake2_128Concat, CollectionId>,860 Key<Blake2_128Concat, T::CrossAccountId>,861 ),862 Value = bool,863 QueryKind = ValueQuery,864 >;865866 867 #[pallet::storage]868 pub type Allowlist<T: Config> = StorageNMap<869 Key = (870 Key<Blake2_128Concat, CollectionId>,871 Key<Blake2_128Concat, T::CrossAccountId>,872 ),873 Value = bool,874 QueryKind = ValueQuery,875 >;876877 878 #[pallet::storage]879 pub type DummyStorageValue<T: Config> = StorageValue<880 Value = (881 CollectionStats,882 CollectionId,883 TokenId,884 TokenChild,885 PhantomType<(886 TokenData<T::CrossAccountId>,887 RpcCollection<T::AccountId>,888 889 RmrkCollectionInfo<T::AccountId>,890 RmrkInstanceInfo<T::AccountId>,891 RmrkResourceInfo,892 RmrkPropertyInfo,893 RmrkBaseInfo<T::AccountId>,894 RmrkPartType,895 RmrkBoundedTheme,896 RmrkNftChild,897 898 PovInfo,899 )>,900 ),901 QueryKind = OptionQuery,902 >;903904 #[pallet::hooks]905 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {906 fn on_runtime_upgrade() -> Weight {907 StorageVersion::new(1).put::<Pallet<T>>();908909 Weight::zero()910 }911 }912}913914impl<T: Config> Pallet<T> {915 916 917 918 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {919 ensure!(920 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,921 <Error<T>>::AddressIsZero922 );923 Ok(())924 }925926 927 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {928 <IsAdmin<T>>::iter_prefix((collection,))929 .map(|(a, _)| a)930 .collect()931 }932933 934 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {935 <Allowlist<T>>::iter_prefix((collection,))936 .map(|(a, _)| a)937 .collect()938 }939940 941 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {942 <Allowlist<T>>::get((collection, user))943 }944945 946 pub fn collection_stats() -> CollectionStats {947 let created = <CreatedCollectionCount<T>>::get();948 let destroyed = <DestroyedCollectionCount<T>>::get();949 CollectionStats {950 created: created.0,951 destroyed: destroyed.0,952 alive: created.0 - destroyed.0,953 }954 }955956 957 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {958 let collection = <CollectionById<T>>::get(collection)?;959 let limits = collection.limits;960 let effective_limits = CollectionLimits {961 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),962 sponsored_data_size: Some(limits.sponsored_data_size()),963 sponsored_data_rate_limit: Some(964 limits965 .sponsored_data_rate_limit966 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),967 ),968 token_limit: Some(limits.token_limit()),969 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(970 match collection.mode {971 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,972 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,973 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,974 },975 )),976 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),977 owner_can_transfer: Some(limits.owner_can_transfer()),978 owner_can_destroy: Some(limits.owner_can_destroy()),979 transfers_enabled: Some(limits.transfers_enabled()),980 };981982 Some(effective_limits)983 }984985 986 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {987 let Collection {988 name,989 description,990 owner,991 mode,992 token_prefix,993 sponsorship,994 limits,995 permissions,996 flags,997 } = <CollectionById<T>>::get(collection)?;998999 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)1000 .into_iter()1001 .map(|(key, permission)| PropertyKeyPermission { key, permission })1002 .collect();10031004 let properties = <CollectionProperties<T>>::get(collection)1005 .into_iter()1006 .map(|(key, value)| Property { key, value })1007 .collect();10081009 let permissions = CollectionPermissions {1010 access: Some(permissions.access()),1011 mint_mode: Some(permissions.mint_mode()),1012 nesting: Some(permissions.nesting().clone()),1013 };10141015 Some(RpcCollection {1016 name: name.into_inner(),1017 description: description.into_inner(),1018 owner,1019 mode,1020 token_prefix: token_prefix.into_inner(),1021 sponsorship,1022 limits,1023 permissions,1024 token_property_permissions,1025 properties,1026 read_only: flags.external,10271028 flags: RpcCollectionFlags {1029 foreign: flags.foreign,1030 erc721metadata: flags.erc721metadata,1031 },1032 })1033 }1034}10351036macro_rules! limit_default {1037 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1038 $(1039 if let Some($new) = $new.$field {1040 let $old = $old.$field($($arg)?);1041 let _ = $new;1042 let _ = $old;1043 $check1044 } else {1045 $new.$field = $old.$field1046 }1047 )*1048 }};1049}1050macro_rules! limit_default_clone {1051 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1052 $(1053 if let Some($new) = $new.$field.clone() {1054 let $old = $old.$field($($arg)?);1055 let _ = $new;1056 let _ = $old;1057 $check1058 } else {1059 $new.$field = $old.$field.clone()1060 }1061 )*1062 }};1063}10641065impl<T: Config> Pallet<T> {1066 1067 1068 1069 1070 1071 pub fn init_collection(1072 owner: T::CrossAccountId,1073 payer: T::CrossAccountId,1074 data: CreateCollectionData<T::AccountId>,1075 flags: CollectionFlags,1076 ) -> Result<CollectionId, DispatchError> {1077 {1078 ensure!(1079 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1080 Error::<T>::CollectionTokenPrefixLimitExceeded1081 );1082 }10831084 let created_count = <CreatedCollectionCount<T>>::get()1085 .01086 .checked_add(1)1087 .ok_or(ArithmeticError::Overflow)?;1088 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1089 let id = CollectionId(created_count);10901091 1092 ensure!(1093 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1094 <Error<T>>::TotalCollectionsLimitExceeded1095 );10961097 10981099 let collection = Collection {1100 owner: owner.as_sub().clone(),1101 name: data.name,1102 mode: data.mode.clone(),1103 description: data.description,1104 token_prefix: data.token_prefix,1105 sponsorship: data1106 .pending_sponsor1107 .map(SponsorshipState::Unconfirmed)1108 .unwrap_or_default(),1109 limits: data1110 .limits1111 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1112 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1113 permissions: data1114 .permissions1115 .map(|permissions| {1116 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1117 })1118 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1119 flags,1120 };11211122 let mut collection_properties = up_data_structs::CollectionProperties::get();1123 collection_properties1124 .try_set_from_iter(data.properties.into_iter())1125 .map_err(<Error<T>>::from)?;11261127 CollectionProperties::<T>::insert(id, collection_properties);11281129 let mut token_props_permissions = PropertiesPermissionMap::new();1130 token_props_permissions1131 .try_set_from_iter(data.token_property_permissions.into_iter())1132 .map_err(<Error<T>>::from)?;11331134 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11351136 1137 {1138 let mut imbalance =1139 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1140 imbalance.subsume(1141 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1142 &T::TreasuryAccountId::get(),1143 T::CollectionCreationPrice::get(),1144 ),1145 );1146 <T as Config>::Currency::settle(1147 payer.as_sub(),1148 imbalance,1149 WithdrawReasons::TRANSFER,1150 ExistenceRequirement::KeepAlive,1151 )1152 .map_err(|_| Error::<T>::NotSufficientFounds)?;1153 }11541155 <CreatedCollectionCount<T>>::put(created_count);1156 <Pallet<T>>::deposit_event(Event::CollectionCreated(1157 id,1158 data.mode.id(),1159 owner.as_sub().clone(),1160 ));1161 <PalletEvm<T>>::deposit_log(1162 erc::CollectionHelpersEvents::CollectionCreated {1163 owner: *owner.as_eth(),1164 collection_id: eth::collection_id_to_address(id),1165 }1166 .to_log(T::ContractAddress::get()),1167 );1168 <CollectionById<T>>::insert(id, collection);1169 Ok(id)1170 }11711172 1173 1174 1175 1176 pub fn destroy_collection(1177 collection: CollectionHandle<T>,1178 sender: &T::CrossAccountId,1179 ) -> DispatchResult {1180 ensure!(1181 collection.limits.owner_can_destroy(),1182 <Error<T>>::NoPermission,1183 );1184 collection.check_is_owner(sender)?;11851186 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1187 .01188 .checked_add(1)1189 .ok_or(ArithmeticError::Overflow)?;11901191 11921193 <DestroyedCollectionCount<T>>::put(destroyed_collections);1194 <CollectionById<T>>::remove(collection.id);1195 <AdminAmount<T>>::remove(collection.id);1196 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1197 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1198 <CollectionProperties<T>>::remove(collection.id);11991200 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));12011202 <PalletEvm<T>>::deposit_log(1203 erc::CollectionHelpersEvents::CollectionDestroyed {1204 collection_id: eth::collection_id_to_address(collection.id),1205 }1206 .to_log(T::ContractAddress::get()),1207 );1208 Ok(())1209 }12101211 1212 1213 1214 1215 1216 1217 1218 1219 #[transactional]1220 fn modify_collection_properties(1221 collection: &CollectionHandle<T>,1222 sender: &T::CrossAccountId,1223 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1224 ) -> DispatchResult {1225 collection.check_is_owner_or_admin(sender)?;12261227 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12281229 for (key, value) in properties_updates {1230 match value {1231 Some(value) => {1232 stored_properties1233 .try_set(key.clone(), value)1234 .map_err(<Error<T>>::from)?;12351236 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1237 <PalletEvm<T>>::deposit_log(1238 erc::CollectionHelpersEvents::CollectionChanged {1239 collection_id: eth::collection_id_to_address(collection.id),1240 }1241 .to_log(T::ContractAddress::get()),1242 );1243 }1244 None => {1245 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12461247 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1248 <PalletEvm<T>>::deposit_log(1249 erc::CollectionHelpersEvents::CollectionChanged {1250 collection_id: eth::collection_id_to_address(collection.id),1251 }1252 .to_log(T::ContractAddress::get()),1253 );1254 }1255 }1256 }12571258 <CollectionProperties<T>>::set(collection.id, stored_properties);12591260 Ok(())1261 }12621263 1264 1265 1266 1267 1268 pub fn set_collection_property(1269 collection: &CollectionHandle<T>,1270 sender: &T::CrossAccountId,1271 property: Property,1272 ) -> DispatchResult {1273 Self::set_collection_properties(collection, sender, [property].into_iter())1274 }12751276 1277 1278 1279 1280 1281 1282 pub fn set_scoped_collection_property(1283 collection_id: CollectionId,1284 scope: PropertyScope,1285 property: Property,1286 ) -> DispatchResult {1287 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1288 properties.try_scoped_set(scope, property.key, property.value)1289 })1290 .map_err(<Error<T>>::from)?;12911292 Ok(())1293 }12941295 1296 1297 1298 1299 1300 1301 pub fn set_scoped_collection_properties(1302 collection_id: CollectionId,1303 scope: PropertyScope,1304 properties: impl Iterator<Item = Property>,1305 ) -> DispatchResult {1306 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1307 stored_properties.try_scoped_set_from_iter(scope, properties)1308 })1309 .map_err(<Error<T>>::from)?;13101311 Ok(())1312 }13131314 1315 1316 1317 1318 1319 pub fn set_collection_properties(1320 collection: &CollectionHandle<T>,1321 sender: &T::CrossAccountId,1322 properties: impl Iterator<Item = Property>,1323 ) -> DispatchResult {1324 Self::modify_collection_properties(1325 collection,1326 sender,1327 properties.map(|property| (property.key, Some(property.value))),1328 )1329 }13301331 1332 1333 1334 1335 1336 pub fn delete_collection_property(1337 collection: &CollectionHandle<T>,1338 sender: &T::CrossAccountId,1339 property_key: PropertyKey,1340 ) -> DispatchResult {1341 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1342 }13431344 1345 1346 1347 1348 1349 pub fn delete_collection_properties(1350 collection: &CollectionHandle<T>,1351 sender: &T::CrossAccountId,1352 property_keys: impl Iterator<Item = PropertyKey>,1353 ) -> DispatchResult {1354 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1355 }13561357 1358 1359 1360 1361 1362 1363 pub fn set_property_permission_unchecked(1364 collection: CollectionId,1365 property_permission: PropertyKeyPermission,1366 ) -> DispatchResult {1367 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1368 permissions.try_set(property_permission.key, property_permission.permission)1369 })1370 .map_err(<Error<T>>::from)?;1371 Ok(())1372 }13731374 1375 1376 1377 1378 1379 pub fn set_property_permission(1380 collection: &CollectionHandle<T>,1381 sender: &T::CrossAccountId,1382 property_permission: PropertyKeyPermission,1383 ) -> DispatchResult {1384 Self::set_scoped_property_permission(1385 collection,1386 sender,1387 PropertyScope::None,1388 property_permission,1389 )1390 }13911392 1393 1394 1395 1396 1397 1398 pub fn set_scoped_property_permission(1399 collection: &CollectionHandle<T>,1400 sender: &T::CrossAccountId,1401 scope: PropertyScope,1402 property_permission: PropertyKeyPermission,1403 ) -> DispatchResult {1404 collection.check_is_owner_or_admin(sender)?;14051406 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1407 let current_permission = all_permissions.get(&property_permission.key);1408 if matches![1409 current_permission,1410 Some(PropertyPermission { mutable: false, .. })1411 ] {1412 return Err(<Error<T>>::NoPermission.into());1413 }14141415 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1416 let property_permission = property_permission.clone();1417 permissions.try_scoped_set(1418 scope,1419 property_permission.key,1420 property_permission.permission,1421 )1422 })1423 .map_err(<Error<T>>::from)?;14241425 Self::deposit_event(Event::PropertyPermissionSet(1426 collection.id,1427 property_permission.key,1428 ));1429 <PalletEvm<T>>::deposit_log(1430 erc::CollectionHelpersEvents::CollectionChanged {1431 collection_id: eth::collection_id_to_address(collection.id),1432 }1433 .to_log(T::ContractAddress::get()),1434 );14351436 Ok(())1437 }14381439 1440 1441 1442 1443 1444 #[transactional]1445 pub fn set_token_property_permissions(1446 collection: &CollectionHandle<T>,1447 sender: &T::CrossAccountId,1448 property_permissions: Vec<PropertyKeyPermission>,1449 ) -> DispatchResult {1450 Self::set_scoped_token_property_permissions(1451 collection,1452 sender,1453 PropertyScope::None,1454 property_permissions,1455 )1456 }14571458 1459 1460 1461 1462 1463 1464 #[transactional]1465 pub fn set_scoped_token_property_permissions(1466 collection: &CollectionHandle<T>,1467 sender: &T::CrossAccountId,1468 scope: PropertyScope,1469 property_permissions: Vec<PropertyKeyPermission>,1470 ) -> DispatchResult {1471 for prop_pemission in property_permissions {1472 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1473 }14741475 Ok(())1476 }14771478 1479 pub fn get_collection_property(1480 collection_id: CollectionId,1481 key: &PropertyKey,1482 ) -> Option<PropertyValue> {1483 Self::collection_properties(collection_id).get(key).cloned()1484 }14851486 1487 pub fn bytes_keys_to_property_keys(1488 keys: Vec<Vec<u8>>,1489 ) -> Result<Vec<PropertyKey>, DispatchError> {1490 keys.into_iter()1491 .map(|key| -> Result<PropertyKey, DispatchError> {1492 key.try_into()1493 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1494 })1495 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1496 }14971498 1499 pub fn filter_collection_properties(1500 collection_id: CollectionId,1501 keys: Option<Vec<PropertyKey>>,1502 ) -> Result<Vec<Property>, DispatchError> {1503 let properties = Self::collection_properties(collection_id);15041505 let properties = keys1506 .map(|keys| {1507 keys.into_iter()1508 .filter_map(|key| {1509 properties.get(&key).map(|value| Property {1510 key,1511 value: value.clone(),1512 })1513 })1514 .collect()1515 })1516 .unwrap_or_else(|| {1517 properties1518 .into_iter()1519 .map(|(key, value)| Property { key, value })1520 .collect()1521 });15221523 Ok(properties)1524 }15251526 1527 pub fn filter_property_permissions(1528 collection_id: CollectionId,1529 keys: Option<Vec<PropertyKey>>,1530 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1531 let permissions = Self::property_permissions(collection_id);15321533 let key_permissions = keys1534 .map(|keys| {1535 keys.into_iter()1536 .filter_map(|key| {1537 permissions1538 .get(&key)1539 .map(|permission| PropertyKeyPermission {1540 key,1541 permission: permission.clone(),1542 })1543 })1544 .collect()1545 })1546 .unwrap_or_else(|| {1547 permissions1548 .into_iter()1549 .map(|(key, permission)| PropertyKeyPermission { key, permission })1550 .collect()1551 });15521553 Ok(key_permissions)1554 }15551556 1557 1558 1559 pub fn toggle_allowlist(1560 collection: &CollectionHandle<T>,1561 sender: &T::CrossAccountId,1562 user: &T::CrossAccountId,1563 allowed: bool,1564 ) -> DispatchResult {1565 collection.check_is_owner_or_admin(sender)?;15661567 15681569 if allowed {1570 <Allowlist<T>>::insert((collection.id, user), true);1571 Self::deposit_event(Event::<T>::AllowListAddressAdded(1572 collection.id,1573 user.clone(),1574 ));1575 } else {1576 <Allowlist<T>>::remove((collection.id, user));1577 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1578 collection.id,1579 user.clone(),1580 ));1581 }15821583 <PalletEvm<T>>::deposit_log(1584 erc::CollectionHelpersEvents::CollectionChanged {1585 collection_id: eth::collection_id_to_address(collection.id),1586 }1587 .to_log(T::ContractAddress::get()),1588 );15891590 Ok(())1591 }15921593 1594 1595 1596 pub fn toggle_admin(1597 collection: &CollectionHandle<T>,1598 sender: &T::CrossAccountId,1599 user: &T::CrossAccountId,1600 admin: bool,1601 ) -> DispatchResult {1602 collection.check_is_internal()?;1603 collection.check_is_owner(sender)?;16041605 let is_admin = <IsAdmin<T>>::get((collection.id, user));1606 if is_admin == admin {1607 if admin {1608 return Ok(());1609 } else {1610 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1611 }1612 }1613 let amount = <AdminAmount<T>>::get(collection.id);16141615 16161617 if admin {1618 let amount = amount1619 .checked_add(1)1620 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1621 ensure!(1622 amount <= Self::collection_admins_limit(),1623 <Error<T>>::CollectionAdminCountExceeded,1624 );16251626 <AdminAmount<T>>::insert(collection.id, amount);1627 <IsAdmin<T>>::insert((collection.id, user), true);16281629 Self::deposit_event(Event::<T>::CollectionAdminAdded(1630 collection.id,1631 user.clone(),1632 ));1633 } else {1634 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1635 <IsAdmin<T>>::remove((collection.id, user));16361637 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1638 collection.id,1639 user.clone(),1640 ));1641 }16421643 <PalletEvm<T>>::deposit_log(1644 erc::CollectionHelpersEvents::CollectionChanged {1645 collection_id: eth::collection_id_to_address(collection.id),1646 }1647 .to_log(T::ContractAddress::get()),1648 );16491650 Ok(())1651 }16521653 1654 pub fn update_limits(1655 user: &T::CrossAccountId,1656 collection: &mut CollectionHandle<T>,1657 new_limit: CollectionLimits,1658 ) -> DispatchResult {1659 collection.check_is_internal()?;1660 collection.check_is_owner_or_admin(user)?;16611662 collection.limits =1663 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16641665 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1666 <PalletEvm<T>>::deposit_log(1667 erc::CollectionHelpersEvents::CollectionChanged {1668 collection_id: eth::collection_id_to_address(collection.id),1669 }1670 .to_log(T::ContractAddress::get()),1671 );16721673 collection.save()1674 }16751676 1677 fn clamp_limits(1678 mode: CollectionMode,1679 old_limit: &CollectionLimits,1680 mut new_limit: CollectionLimits,1681 ) -> Result<CollectionLimits, DispatchError> {1682 let limits = old_limit;1683 limit_default!(old_limit, new_limit,1684 account_token_ownership_limit => ensure!(1685 new_limit <= MAX_TOKEN_OWNERSHIP,1686 <Error<T>>::CollectionLimitBoundsExceeded,1687 ),1688 sponsored_data_size => ensure!(1689 new_limit <= CUSTOM_DATA_LIMIT,1690 <Error<T>>::CollectionLimitBoundsExceeded,1691 ),16921693 sponsored_data_rate_limit => {},1694 token_limit => ensure!(1695 old_limit >= new_limit && new_limit > 0,1696 <Error<T>>::CollectionTokenLimitExceeded1697 ),16981699 sponsor_transfer_timeout(match mode {1700 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1701 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1702 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1703 }) => ensure!(1704 new_limit <= MAX_SPONSOR_TIMEOUT,1705 <Error<T>>::CollectionLimitBoundsExceeded,1706 ),1707 sponsor_approve_timeout => {},1708 owner_can_transfer => ensure!(1709 !limits.owner_can_transfer_instaled() ||1710 old_limit || !new_limit,1711 <Error<T>>::OwnerPermissionsCantBeReverted,1712 ),1713 owner_can_destroy => ensure!(1714 old_limit || !new_limit,1715 <Error<T>>::OwnerPermissionsCantBeReverted,1716 ),1717 transfers_enabled => {},1718 );1719 Ok(new_limit)1720 }17211722 1723 pub fn update_permissions(1724 user: &T::CrossAccountId,1725 collection: &mut CollectionHandle<T>,1726 new_permission: CollectionPermissions,1727 ) -> DispatchResult {1728 collection.check_is_internal()?;1729 collection.check_is_owner_or_admin(user)?;1730 collection.permissions = Self::clamp_permissions(1731 collection.mode.clone(),1732 &collection.permissions,1733 new_permission,1734 )?;17351736 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1737 <PalletEvm<T>>::deposit_log(1738 erc::CollectionHelpersEvents::CollectionChanged {1739 collection_id: eth::collection_id_to_address(collection.id),1740 }1741 .to_log(T::ContractAddress::get()),1742 );17431744 collection.save()1745 }17461747 1748 fn clamp_permissions(1749 _mode: CollectionMode,1750 old_permission: &CollectionPermissions,1751 mut new_permission: CollectionPermissions,1752 ) -> Result<CollectionPermissions, DispatchError> {1753 limit_default_clone!(old_permission, new_permission,1754 access => {},1755 mint_mode => {},1756 nesting => { },1757 );1758 Ok(new_permission)1759 }17601761 1762 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1763 CollectionProperties::<T>::mutate(collection_id, |properties| {1764 properties.recompute_consumed_space();1765 });17661767 Ok(())1768 }1769}177017711772#[macro_export]1773macro_rules! unsupported {1774 ($runtime:path) => {1775 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1776 };1777}177817791780pub trait CommonWeightInfo<CrossAccountId> {1781 1782 fn create_item() -> Weight;17831784 1785 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17861787 1788 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17891790 1791 fn burn_item() -> Weight;17921793 1794 1795 1796 fn set_collection_properties(amount: u32) -> Weight;17971798 1799 1800 1801 fn delete_collection_properties(amount: u32) -> Weight;18021803 1804 1805 1806 fn set_token_properties(amount: u32) -> Weight;18071808 1809 1810 1811 fn delete_token_properties(amount: u32) -> Weight;18121813 1814 1815 1816 fn set_token_property_permissions(amount: u32) -> Weight;18171818 1819 fn transfer() -> Weight;18201821 1822 fn approve() -> Weight;18231824 1825 fn approve_from() -> Weight;18261827 1828 fn transfer_from() -> Weight;18291830 1831 fn burn_from() -> Weight;18321833 1834 1835 1836 1837 fn burn_recursively_self_raw() -> Weight;18381839 1840 1841 1842 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18431844 1845 1846 1847 1848 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1849 Self::burn_recursively_self_raw()1850 .saturating_mul(max_selfs.max(1) as u64)1851 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1852 }18531854 1855 fn token_owner() -> Weight;18561857 1858 fn set_allowance_for_all() -> Weight;18591860 1861 fn force_repair_item() -> Weight;1862}186318641865pub trait RefungibleExtensionsWeightInfo {1866 1867 fn repartition() -> Weight;1868}186918701871187218731874pub trait CommonCollectionOperations<T: Config> {1875 1876 1877 1878 1879 1880 1881 fn create_item(1882 &self,1883 sender: T::CrossAccountId,1884 to: T::CrossAccountId,1885 data: CreateItemData,1886 nesting_budget: &dyn Budget,1887 ) -> DispatchResultWithPostInfo;18881889 1890 1891 1892 1893 1894 1895 fn create_multiple_items(1896 &self,1897 sender: T::CrossAccountId,1898 to: T::CrossAccountId,1899 data: Vec<CreateItemData>,1900 nesting_budget: &dyn Budget,1901 ) -> DispatchResultWithPostInfo;19021903 1904 1905 1906 1907 1908 1909 fn create_multiple_items_ex(1910 &self,1911 sender: T::CrossAccountId,1912 data: CreateItemExData<T::CrossAccountId>,1913 nesting_budget: &dyn Budget,1914 ) -> DispatchResultWithPostInfo;19151916 1917 1918 1919 1920 1921 fn burn_item(1922 &self,1923 sender: T::CrossAccountId,1924 token: TokenId,1925 amount: u128,1926 ) -> DispatchResultWithPostInfo;19271928 1929 1930 1931 1932 1933 1934 fn burn_item_recursively(1935 &self,1936 sender: T::CrossAccountId,1937 token: TokenId,1938 self_budget: &dyn Budget,1939 breadth_budget: &dyn Budget,1940 ) -> DispatchResultWithPostInfo;19411942 1943 1944 1945 1946 fn set_collection_properties(1947 &self,1948 sender: T::CrossAccountId,1949 properties: Vec<Property>,1950 ) -> DispatchResultWithPostInfo;19511952 1953 1954 1955 1956 fn delete_collection_properties(1957 &self,1958 sender: &T::CrossAccountId,1959 property_keys: Vec<PropertyKey>,1960 ) -> DispatchResultWithPostInfo;19611962 1963 1964 1965 1966 1967 1968 1969 1970 1971 fn set_token_properties(1972 &self,1973 sender: T::CrossAccountId,1974 token_id: TokenId,1975 properties: Vec<Property>,1976 budget: &dyn Budget,1977 ) -> DispatchResultWithPostInfo;19781979 1980 1981 1982 1983 1984 1985 1986 1987 1988 fn delete_token_properties(1989 &self,1990 sender: T::CrossAccountId,1991 token_id: TokenId,1992 property_keys: Vec<PropertyKey>,1993 budget: &dyn Budget,1994 ) -> DispatchResultWithPostInfo;19951996 1997 1998 1999 2000 2001 2002 fn set_token_property_permissions(2003 &self,2004 sender: &T::CrossAccountId,2005 property_permissions: Vec<PropertyKeyPermission>,2006 ) -> DispatchResultWithPostInfo;20072008 2009 2010 2011 2012 2013 2014 2015 fn transfer(2016 &self,2017 sender: T::CrossAccountId,2018 to: T::CrossAccountId,2019 token: TokenId,2020 amount: u128,2021 budget: &dyn Budget,2022 ) -> DispatchResultWithPostInfo;20232024 2025 2026 2027 2028 2029 2030 fn approve(2031 &self,2032 sender: T::CrossAccountId,2033 spender: T::CrossAccountId,2034 token: TokenId,2035 amount: u128,2036 ) -> DispatchResultWithPostInfo;20372038 2039 2040 2041 2042 2043 2044 2045 fn approve_from(2046 &self,2047 sender: T::CrossAccountId,2048 from: T::CrossAccountId,2049 to: T::CrossAccountId,2050 token: TokenId,2051 amount: u128,2052 ) -> DispatchResultWithPostInfo;20532054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 fn transfer_from(2065 &self,2066 sender: T::CrossAccountId,2067 from: T::CrossAccountId,2068 to: T::CrossAccountId,2069 token: TokenId,2070 amount: u128,2071 budget: &dyn Budget,2072 ) -> DispatchResultWithPostInfo;20732074 2075 2076 2077 2078 2079 2080 2081 2082 2083 fn burn_from(2084 &self,2085 sender: T::CrossAccountId,2086 from: T::CrossAccountId,2087 token: TokenId,2088 amount: u128,2089 budget: &dyn Budget,2090 ) -> DispatchResultWithPostInfo;20912092 2093 2094 2095 2096 2097 2098 fn check_nesting(2099 &self,2100 sender: T::CrossAccountId,2101 from: (CollectionId, TokenId),2102 under: TokenId,2103 budget: &dyn Budget,2104 ) -> DispatchResult;21052106 2107 2108 2109 2110 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21112112 2113 2114 2115 2116 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21172118 2119 2120 2121 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21222123 2124 fn collection_tokens(&self) -> Vec<TokenId>;21252126 2127 2128 2129 fn token_exists(&self, token: TokenId) -> bool;21302131 2132 fn last_token_id(&self) -> TokenId;21332134 2135 2136 2137 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;21382139 2140 2141 2142 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;21432144 2145 2146 2147 2148 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21492150 2151 2152 2153 2154 2155 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21562157 2158 fn total_supply(&self) -> u32;21592160 2161 2162 2163 fn account_balance(&self, account: T::CrossAccountId) -> u32;21642165 2166 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21672168 2169 fn total_pieces(&self, token: TokenId) -> Option<u128>;21702171 2172 2173 2174 2175 2176 fn allowance(2177 &self,2178 sender: T::CrossAccountId,2179 spender: T::CrossAccountId,2180 token: TokenId,2181 ) -> u128;21822183 2184 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21852186 2187 2188 2189 2190 fn set_allowance_for_all(2191 &self,2192 owner: T::CrossAccountId,2193 operator: T::CrossAccountId,2194 approve: bool,2195 ) -> DispatchResultWithPostInfo;21962197 2198 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21992200 2201 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2202}220322042205pub trait RefungibleExtensions<T>2206where2207 T: Config,2208{2209 2210 2211 2212 2213 2214 2215 2216 fn repartition(2217 &self,2218 sender: &T::CrossAccountId,2219 token: TokenId,2220 amount: u128,2221 ) -> DispatchResultWithPostInfo;2222}22232224222522262227pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2228 let post_info = PostDispatchInfo {2229 actual_weight: Some(weight),2230 pays_fee: Pays::Yes,2231 };2232 match res {2233 Ok(()) => Ok(post_info),2234 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2235 }2236}22372238impl<T: Config> From<PropertiesError> for Error<T> {2239 fn from(error: PropertiesError) -> Self {2240 match error {2241 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2242 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2243 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2244 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2245 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2246 }2247 }2248}