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,749750 751 AddressIsZero,752753 754 UnsupportedOperation,755756 757 NotSufficientFounds,758759 760 UserIsNotAllowedToNest,761 762 SourceCollectionIsNotAllowedToNest,763764 765 CollectionFieldSizeExceeded,766767 768 NoSpaceForProperty,769770 771 PropertyLimitReached,772773 774 PropertyKeyIsTooLong,775776 777 InvalidCharacterInPropertyKey,778779 780 EmptyPropertyKey,781782 783 CollectionIsExternal,784785 786 CollectionIsInternal,787788 789 ConfirmSponsorshipFail,790791 792 UserIsNotCollectionAdmin,793 }794795 796 #[pallet::storage]797 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;798799 800 #[pallet::storage]801 pub type DestroyedCollectionCount<T> =802 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;803804 805 #[pallet::storage]806 pub type CollectionById<T> = StorageMap<807 Hasher = Blake2_128Concat,808 Key = CollectionId,809 Value = Collection<<T as frame_system::Config>::AccountId>,810 QueryKind = OptionQuery,811 >;812813 814 #[pallet::storage]815 #[pallet::getter(fn collection_properties)]816 pub type CollectionProperties<T> = StorageMap<817 Hasher = Blake2_128Concat,818 Key = CollectionId,819 Value = Properties,820 QueryKind = ValueQuery,821 OnEmpty = up_data_structs::CollectionProperties,822 >;823824 825 #[pallet::storage]826 #[pallet::getter(fn property_permissions)]827 pub type CollectionPropertyPermissions<T> = StorageMap<828 Hasher = Blake2_128Concat,829 Key = CollectionId,830 Value = PropertiesPermissionMap,831 QueryKind = ValueQuery,832 >;833834 835 #[pallet::storage]836 pub type AdminAmount<T> = StorageMap<837 Hasher = Blake2_128Concat,838 Key = CollectionId,839 Value = u32,840 QueryKind = ValueQuery,841 >;842843 844 #[pallet::storage]845 pub type IsAdmin<T: Config> = StorageNMap<846 Key = (847 Key<Blake2_128Concat, CollectionId>,848 Key<Blake2_128Concat, T::CrossAccountId>,849 ),850 Value = bool,851 QueryKind = ValueQuery,852 >;853854 855 #[pallet::storage]856 pub type Allowlist<T: Config> = StorageNMap<857 Key = (858 Key<Blake2_128Concat, CollectionId>,859 Key<Blake2_128Concat, T::CrossAccountId>,860 ),861 Value = bool,862 QueryKind = ValueQuery,863 >;864865 866 #[pallet::storage]867 pub type DummyStorageValue<T: Config> = StorageValue<868 Value = (869 CollectionStats,870 CollectionId,871 TokenId,872 TokenChild,873 PhantomType<(874 TokenData<T::CrossAccountId>,875 RpcCollection<T::AccountId>,876 877 RmrkCollectionInfo<T::AccountId>,878 RmrkInstanceInfo<T::AccountId>,879 RmrkResourceInfo,880 RmrkPropertyInfo,881 RmrkBaseInfo<T::AccountId>,882 RmrkPartType,883 RmrkBoundedTheme,884 RmrkNftChild,885 886 PovInfo,887 )>,888 ),889 QueryKind = OptionQuery,890 >;891892 #[pallet::hooks]893 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {894 fn on_runtime_upgrade() -> Weight {895 StorageVersion::new(1).put::<Pallet<T>>();896897 Weight::zero()898 }899 }900}901902impl<T: Config> Pallet<T> {903 904 905 906 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {907 ensure!(908 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,909 <Error<T>>::AddressIsZero910 );911 Ok(())912 }913914 915 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {916 <IsAdmin<T>>::iter_prefix((collection,))917 .map(|(a, _)| a)918 .collect()919 }920921 922 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {923 <Allowlist<T>>::iter_prefix((collection,))924 .map(|(a, _)| a)925 .collect()926 }927928 929 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {930 <Allowlist<T>>::get((collection, user))931 }932933 934 pub fn collection_stats() -> CollectionStats {935 let created = <CreatedCollectionCount<T>>::get();936 let destroyed = <DestroyedCollectionCount<T>>::get();937 CollectionStats {938 created: created.0,939 destroyed: destroyed.0,940 alive: created.0 - destroyed.0,941 }942 }943944 945 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {946 let collection = <CollectionById<T>>::get(collection)?;947 let limits = collection.limits;948 let effective_limits = CollectionLimits {949 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),950 sponsored_data_size: Some(limits.sponsored_data_size()),951 sponsored_data_rate_limit: Some(952 limits953 .sponsored_data_rate_limit954 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),955 ),956 token_limit: Some(limits.token_limit()),957 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(958 match collection.mode {959 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,960 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,961 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,962 },963 )),964 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),965 owner_can_transfer: Some(limits.owner_can_transfer()),966 owner_can_destroy: Some(limits.owner_can_destroy()),967 transfers_enabled: Some(limits.transfers_enabled()),968 };969970 Some(effective_limits)971 }972973 974 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {975 let Collection {976 name,977 description,978 owner,979 mode,980 token_prefix,981 sponsorship,982 limits,983 permissions,984 flags,985 } = <CollectionById<T>>::get(collection)?;986987 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)988 .into_iter()989 .map(|(key, permission)| PropertyKeyPermission { key, permission })990 .collect();991992 let properties = <CollectionProperties<T>>::get(collection)993 .into_iter()994 .map(|(key, value)| Property { key, value })995 .collect();996997 let permissions = CollectionPermissions {998 access: Some(permissions.access()),999 mint_mode: Some(permissions.mint_mode()),1000 nesting: Some(permissions.nesting().clone()),1001 };10021003 Some(RpcCollection {1004 name: name.into_inner(),1005 description: description.into_inner(),1006 owner,1007 mode,1008 token_prefix: token_prefix.into_inner(),1009 sponsorship,1010 limits,1011 permissions,1012 token_property_permissions,1013 properties,1014 read_only: flags.external,10151016 flags: RpcCollectionFlags {1017 foreign: flags.foreign,1018 erc721metadata: flags.erc721metadata,1019 },1020 })1021 }1022}10231024macro_rules! limit_default {1025 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1026 $(1027 if let Some($new) = $new.$field {1028 let $old = $old.$field($($arg)?);1029 let _ = $new;1030 let _ = $old;1031 $check1032 } else {1033 $new.$field = $old.$field1034 }1035 )*1036 }};1037}1038macro_rules! limit_default_clone {1039 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1040 $(1041 if let Some($new) = $new.$field.clone() {1042 let $old = $old.$field($($arg)?);1043 let _ = $new;1044 let _ = $old;1045 $check1046 } else {1047 $new.$field = $old.$field.clone()1048 }1049 )*1050 }};1051}10521053impl<T: Config> Pallet<T> {1054 1055 1056 1057 1058 1059 pub fn init_collection(1060 owner: T::CrossAccountId,1061 payer: T::CrossAccountId,1062 data: CreateCollectionData<T::AccountId>,1063 flags: CollectionFlags,1064 ) -> Result<CollectionId, DispatchError> {1065 {1066 ensure!(1067 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1068 Error::<T>::CollectionTokenPrefixLimitExceeded1069 );1070 }10711072 let created_count = <CreatedCollectionCount<T>>::get()1073 .01074 .checked_add(1)1075 .ok_or(ArithmeticError::Overflow)?;1076 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1077 let id = CollectionId(created_count);10781079 1080 ensure!(1081 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1082 <Error<T>>::TotalCollectionsLimitExceeded1083 );10841085 10861087 let collection = Collection {1088 owner: owner.as_sub().clone(),1089 name: data.name,1090 mode: data.mode.clone(),1091 description: data.description,1092 token_prefix: data.token_prefix,1093 sponsorship: data1094 .pending_sponsor1095 .map(SponsorshipState::Unconfirmed)1096 .unwrap_or_default(),1097 limits: data1098 .limits1099 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1100 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1101 permissions: data1102 .permissions1103 .map(|permissions| {1104 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1105 })1106 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1107 flags,1108 };11091110 let mut collection_properties = up_data_structs::CollectionProperties::get();1111 collection_properties1112 .try_set_from_iter(data.properties.into_iter())1113 .map_err(<Error<T>>::from)?;11141115 CollectionProperties::<T>::insert(id, collection_properties);11161117 let mut token_props_permissions = PropertiesPermissionMap::new();1118 token_props_permissions1119 .try_set_from_iter(data.token_property_permissions.into_iter())1120 .map_err(<Error<T>>::from)?;11211122 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11231124 1125 {1126 let mut imbalance =1127 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1128 imbalance.subsume(1129 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1130 &T::TreasuryAccountId::get(),1131 T::CollectionCreationPrice::get(),1132 ),1133 );1134 <T as Config>::Currency::settle(1135 payer.as_sub(),1136 imbalance,1137 WithdrawReasons::TRANSFER,1138 ExistenceRequirement::KeepAlive,1139 )1140 .map_err(|_| Error::<T>::NotSufficientFounds)?;1141 }11421143 <CreatedCollectionCount<T>>::put(created_count);1144 <Pallet<T>>::deposit_event(Event::CollectionCreated(1145 id,1146 data.mode.id(),1147 owner.as_sub().clone(),1148 ));1149 <PalletEvm<T>>::deposit_log(1150 erc::CollectionHelpersEvents::CollectionCreated {1151 owner: *owner.as_eth(),1152 collection_id: eth::collection_id_to_address(id),1153 }1154 .to_log(T::ContractAddress::get()),1155 );1156 <CollectionById<T>>::insert(id, collection);1157 Ok(id)1158 }11591160 1161 1162 1163 1164 pub fn destroy_collection(1165 collection: CollectionHandle<T>,1166 sender: &T::CrossAccountId,1167 ) -> DispatchResult {1168 ensure!(1169 collection.limits.owner_can_destroy(),1170 <Error<T>>::NoPermission,1171 );1172 collection.check_is_owner(sender)?;11731174 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1175 .01176 .checked_add(1)1177 .ok_or(ArithmeticError::Overflow)?;11781179 11801181 <DestroyedCollectionCount<T>>::put(destroyed_collections);1182 <CollectionById<T>>::remove(collection.id);1183 <AdminAmount<T>>::remove(collection.id);1184 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1185 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1186 <CollectionProperties<T>>::remove(collection.id);11871188 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11891190 <PalletEvm<T>>::deposit_log(1191 erc::CollectionHelpersEvents::CollectionDestroyed {1192 collection_id: eth::collection_id_to_address(collection.id),1193 }1194 .to_log(T::ContractAddress::get()),1195 );1196 Ok(())1197 }11981199 1200 1201 1202 1203 1204 pub fn set_collection_property(1205 collection: &CollectionHandle<T>,1206 sender: &T::CrossAccountId,1207 property: Property,1208 ) -> DispatchResult {1209 collection.check_is_owner_or_admin(sender)?;12101211 CollectionProperties::<T>::try_mutate(collection.id, |properties| {1212 let property = property.clone();1213 properties.try_set(property.key, property.value)1214 })1215 .map_err(<Error<T>>::from)?;12161217 Self::deposit_event(Event::CollectionPropertySet(collection.id, property.key));1218 <PalletEvm<T>>::deposit_log(1219 erc::CollectionHelpersEvents::CollectionChanged {1220 collection_id: eth::collection_id_to_address(collection.id),1221 }1222 .to_log(T::ContractAddress::get()),1223 );12241225 Ok(())1226 }12271228 1229 1230 1231 1232 1233 1234 pub fn set_scoped_collection_property(1235 collection_id: CollectionId,1236 scope: PropertyScope,1237 property: Property,1238 ) -> DispatchResult {1239 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1240 properties.try_scoped_set(scope, property.key, property.value)1241 })1242 .map_err(<Error<T>>::from)?;12431244 Ok(())1245 }12461247 1248 1249 1250 1251 1252 1253 pub fn set_scoped_collection_properties(1254 collection_id: CollectionId,1255 scope: PropertyScope,1256 properties: impl Iterator<Item = Property>,1257 ) -> DispatchResult {1258 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1259 stored_properties.try_scoped_set_from_iter(scope, properties)1260 })1261 .map_err(<Error<T>>::from)?;12621263 Ok(())1264 }12651266 1267 1268 1269 1270 1271 #[transactional]1272 pub fn set_collection_properties(1273 collection: &CollectionHandle<T>,1274 sender: &T::CrossAccountId,1275 properties: Vec<Property>,1276 ) -> DispatchResult {1277 for property in properties {1278 Self::set_collection_property(collection, sender, property)?;1279 }12801281 Ok(())1282 }12831284 1285 1286 1287 1288 1289 pub fn delete_collection_property(1290 collection: &CollectionHandle<T>,1291 sender: &T::CrossAccountId,1292 property_key: PropertyKey,1293 ) -> DispatchResult {1294 collection.check_is_owner_or_admin(sender)?;12951296 CollectionProperties::<T>::try_mutate(collection.id, |properties| {1297 properties.remove(&property_key)1298 })1299 .map_err(<Error<T>>::from)?;13001301 Self::deposit_event(Event::CollectionPropertyDeleted(1302 collection.id,1303 property_key,1304 ));1305 <PalletEvm<T>>::deposit_log(1306 erc::CollectionHelpersEvents::CollectionChanged {1307 collection_id: eth::collection_id_to_address(collection.id),1308 }1309 .to_log(T::ContractAddress::get()),1310 );13111312 Ok(())1313 }13141315 1316 1317 1318 1319 1320 #[transactional]1321 pub fn delete_collection_properties(1322 collection: &CollectionHandle<T>,1323 sender: &T::CrossAccountId,1324 property_keys: Vec<PropertyKey>,1325 ) -> DispatchResult {1326 for key in property_keys {1327 Self::delete_collection_property(collection, sender, key)?;1328 }13291330 Ok(())1331 }13321333 1334 1335 1336 1337 1338 1339 pub fn set_property_permission_unchecked(1340 collection: CollectionId,1341 property_permission: PropertyKeyPermission,1342 ) -> DispatchResult {1343 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1344 permissions.try_set(property_permission.key, property_permission.permission)1345 })1346 .map_err(<Error<T>>::from)?;1347 Ok(())1348 }13491350 1351 1352 1353 1354 1355 pub fn set_property_permission(1356 collection: &CollectionHandle<T>,1357 sender: &T::CrossAccountId,1358 property_permission: PropertyKeyPermission,1359 ) -> DispatchResult {1360 Self::set_scoped_property_permission(1361 collection,1362 sender,1363 PropertyScope::None,1364 property_permission,1365 )1366 }13671368 1369 1370 1371 1372 1373 1374 pub fn set_scoped_property_permission(1375 collection: &CollectionHandle<T>,1376 sender: &T::CrossAccountId,1377 scope: PropertyScope,1378 property_permission: PropertyKeyPermission,1379 ) -> DispatchResult {1380 collection.check_is_owner_or_admin(sender)?;13811382 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1383 let current_permission = all_permissions.get(&property_permission.key);1384 if matches![1385 current_permission,1386 Some(PropertyPermission { mutable: false, .. })1387 ] {1388 return Err(<Error<T>>::NoPermission.into());1389 }13901391 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1392 let property_permission = property_permission.clone();1393 permissions.try_scoped_set(1394 scope,1395 property_permission.key,1396 property_permission.permission,1397 )1398 })1399 .map_err(<Error<T>>::from)?;14001401 Self::deposit_event(Event::PropertyPermissionSet(1402 collection.id,1403 property_permission.key,1404 ));1405 <PalletEvm<T>>::deposit_log(1406 erc::CollectionHelpersEvents::CollectionChanged {1407 collection_id: eth::collection_id_to_address(collection.id),1408 }1409 .to_log(T::ContractAddress::get()),1410 );14111412 Ok(())1413 }14141415 1416 1417 1418 1419 1420 #[transactional]1421 pub fn set_token_property_permissions(1422 collection: &CollectionHandle<T>,1423 sender: &T::CrossAccountId,1424 property_permissions: Vec<PropertyKeyPermission>,1425 ) -> DispatchResult {1426 Self::set_scoped_token_property_permissions(1427 collection,1428 sender,1429 PropertyScope::None,1430 property_permissions,1431 )1432 }14331434 1435 1436 1437 1438 1439 1440 #[transactional]1441 pub fn set_scoped_token_property_permissions(1442 collection: &CollectionHandle<T>,1443 sender: &T::CrossAccountId,1444 scope: PropertyScope,1445 property_permissions: Vec<PropertyKeyPermission>,1446 ) -> DispatchResult {1447 for prop_pemission in property_permissions {1448 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1449 }14501451 Ok(())1452 }14531454 1455 pub fn get_collection_property(1456 collection_id: CollectionId,1457 key: &PropertyKey,1458 ) -> Option<PropertyValue> {1459 Self::collection_properties(collection_id).get(key).cloned()1460 }14611462 1463 pub fn bytes_keys_to_property_keys(1464 keys: Vec<Vec<u8>>,1465 ) -> Result<Vec<PropertyKey>, DispatchError> {1466 keys.into_iter()1467 .map(|key| -> Result<PropertyKey, DispatchError> {1468 key.try_into()1469 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1470 })1471 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1472 }14731474 1475 pub fn filter_collection_properties(1476 collection_id: CollectionId,1477 keys: Option<Vec<PropertyKey>>,1478 ) -> Result<Vec<Property>, DispatchError> {1479 let properties = Self::collection_properties(collection_id);14801481 let properties = keys1482 .map(|keys| {1483 keys.into_iter()1484 .filter_map(|key| {1485 properties.get(&key).map(|value| Property {1486 key,1487 value: value.clone(),1488 })1489 })1490 .collect()1491 })1492 .unwrap_or_else(|| {1493 properties1494 .into_iter()1495 .map(|(key, value)| Property { key, value })1496 .collect()1497 });14981499 Ok(properties)1500 }15011502 1503 pub fn filter_property_permissions(1504 collection_id: CollectionId,1505 keys: Option<Vec<PropertyKey>>,1506 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1507 let permissions = Self::property_permissions(collection_id);15081509 let key_permissions = keys1510 .map(|keys| {1511 keys.into_iter()1512 .filter_map(|key| {1513 permissions1514 .get(&key)1515 .map(|permission| PropertyKeyPermission {1516 key,1517 permission: permission.clone(),1518 })1519 })1520 .collect()1521 })1522 .unwrap_or_else(|| {1523 permissions1524 .into_iter()1525 .map(|(key, permission)| PropertyKeyPermission { key, permission })1526 .collect()1527 });15281529 Ok(key_permissions)1530 }15311532 1533 1534 1535 pub fn toggle_allowlist(1536 collection: &CollectionHandle<T>,1537 sender: &T::CrossAccountId,1538 user: &T::CrossAccountId,1539 allowed: bool,1540 ) -> DispatchResult {1541 collection.check_is_owner_or_admin(sender)?;15421543 15441545 if allowed {1546 <Allowlist<T>>::insert((collection.id, user), true);1547 Self::deposit_event(Event::<T>::AllowListAddressAdded(1548 collection.id,1549 user.clone(),1550 ));1551 } else {1552 <Allowlist<T>>::remove((collection.id, user));1553 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1554 collection.id,1555 user.clone(),1556 ));1557 }15581559 <PalletEvm<T>>::deposit_log(1560 erc::CollectionHelpersEvents::CollectionChanged {1561 collection_id: eth::collection_id_to_address(collection.id),1562 }1563 .to_log(T::ContractAddress::get()),1564 );15651566 Ok(())1567 }15681569 1570 1571 1572 pub fn toggle_admin(1573 collection: &CollectionHandle<T>,1574 sender: &T::CrossAccountId,1575 user: &T::CrossAccountId,1576 admin: bool,1577 ) -> DispatchResult {1578 collection.check_is_internal()?;1579 collection.check_is_owner(sender)?;15801581 let is_admin = <IsAdmin<T>>::get((collection.id, user));1582 if is_admin == admin {1583 if admin {1584 return Ok(());1585 } else {1586 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1587 }1588 }1589 let amount = <AdminAmount<T>>::get(collection.id);15901591 15921593 if admin {1594 let amount = amount1595 .checked_add(1)1596 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1597 ensure!(1598 amount <= Self::collection_admins_limit(),1599 <Error<T>>::CollectionAdminCountExceeded,1600 );16011602 <AdminAmount<T>>::insert(collection.id, amount);1603 <IsAdmin<T>>::insert((collection.id, user), true);16041605 Self::deposit_event(Event::<T>::CollectionAdminAdded(1606 collection.id,1607 user.clone(),1608 ));1609 } else {1610 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1611 <IsAdmin<T>>::remove((collection.id, user));16121613 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1614 collection.id,1615 user.clone(),1616 ));1617 }16181619 <PalletEvm<T>>::deposit_log(1620 erc::CollectionHelpersEvents::CollectionChanged {1621 collection_id: eth::collection_id_to_address(collection.id),1622 }1623 .to_log(T::ContractAddress::get()),1624 );16251626 Ok(())1627 }16281629 1630 pub fn update_limits(1631 user: &T::CrossAccountId,1632 collection: &mut CollectionHandle<T>,1633 new_limit: CollectionLimits,1634 ) -> DispatchResult {1635 collection.check_is_internal()?;1636 collection.check_is_owner_or_admin(user)?;16371638 collection.limits =1639 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16401641 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1642 <PalletEvm<T>>::deposit_log(1643 erc::CollectionHelpersEvents::CollectionChanged {1644 collection_id: eth::collection_id_to_address(collection.id),1645 }1646 .to_log(T::ContractAddress::get()),1647 );16481649 collection.save()1650 }16511652 1653 fn clamp_limits(1654 mode: CollectionMode,1655 old_limit: &CollectionLimits,1656 mut new_limit: CollectionLimits,1657 ) -> Result<CollectionLimits, DispatchError> {1658 let limits = old_limit;1659 limit_default!(old_limit, new_limit,1660 account_token_ownership_limit => ensure!(1661 new_limit <= MAX_TOKEN_OWNERSHIP,1662 <Error<T>>::CollectionLimitBoundsExceeded,1663 ),1664 sponsored_data_size => ensure!(1665 new_limit <= CUSTOM_DATA_LIMIT,1666 <Error<T>>::CollectionLimitBoundsExceeded,1667 ),16681669 sponsored_data_rate_limit => {},1670 token_limit => ensure!(1671 old_limit >= new_limit && new_limit > 0,1672 <Error<T>>::CollectionTokenLimitExceeded1673 ),16741675 sponsor_transfer_timeout(match mode {1676 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1677 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1678 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1679 }) => ensure!(1680 new_limit <= MAX_SPONSOR_TIMEOUT,1681 <Error<T>>::CollectionLimitBoundsExceeded,1682 ),1683 sponsor_approve_timeout => {},1684 owner_can_transfer => ensure!(1685 !limits.owner_can_transfer_instaled() ||1686 old_limit || !new_limit,1687 <Error<T>>::OwnerPermissionsCantBeReverted,1688 ),1689 owner_can_destroy => ensure!(1690 old_limit || !new_limit,1691 <Error<T>>::OwnerPermissionsCantBeReverted,1692 ),1693 transfers_enabled => {},1694 );1695 Ok(new_limit)1696 }16971698 1699 pub fn update_permissions(1700 user: &T::CrossAccountId,1701 collection: &mut CollectionHandle<T>,1702 new_permission: CollectionPermissions,1703 ) -> DispatchResult {1704 collection.check_is_internal()?;1705 collection.check_is_owner_or_admin(user)?;1706 collection.permissions = Self::clamp_permissions(1707 collection.mode.clone(),1708 &collection.permissions,1709 new_permission,1710 )?;17111712 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1713 <PalletEvm<T>>::deposit_log(1714 erc::CollectionHelpersEvents::CollectionChanged {1715 collection_id: eth::collection_id_to_address(collection.id),1716 }1717 .to_log(T::ContractAddress::get()),1718 );17191720 collection.save()1721 }17221723 1724 fn clamp_permissions(1725 _mode: CollectionMode,1726 old_permission: &CollectionPermissions,1727 mut new_permission: CollectionPermissions,1728 ) -> Result<CollectionPermissions, DispatchError> {1729 limit_default_clone!(old_permission, new_permission,1730 access => {},1731 mint_mode => {},1732 nesting => { },1733 );1734 Ok(new_permission)1735 }17361737 1738 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1739 CollectionProperties::<T>::mutate(collection_id, |properties| {1740 properties.recompute_consumed_space();1741 });17421743 Ok(())1744 }1745}174617471748#[macro_export]1749macro_rules! unsupported {1750 ($runtime:path) => {1751 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1752 };1753}175417551756pub trait CommonWeightInfo<CrossAccountId> {1757 1758 fn create_item() -> Weight;17591760 1761 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17621763 1764 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17651766 1767 fn burn_item() -> Weight;17681769 1770 1771 1772 fn set_collection_properties(amount: u32) -> Weight;17731774 1775 1776 1777 fn delete_collection_properties(amount: u32) -> Weight;17781779 1780 1781 1782 fn set_token_properties(amount: u32) -> Weight;17831784 1785 1786 1787 fn delete_token_properties(amount: u32) -> Weight;17881789 1790 1791 1792 fn set_token_property_permissions(amount: u32) -> Weight;17931794 1795 fn transfer() -> Weight;17961797 1798 fn approve() -> Weight;17991800 1801 fn transfer_from() -> Weight;18021803 1804 fn burn_from() -> Weight;18051806 1807 1808 1809 1810 fn burn_recursively_self_raw() -> Weight;18111812 1813 1814 1815 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18161817 1818 1819 1820 1821 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1822 Self::burn_recursively_self_raw()1823 .saturating_mul(max_selfs.max(1) as u64)1824 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1825 }18261827 1828 fn token_owner() -> Weight;18291830 1831 fn set_allowance_for_all() -> Weight;18321833 1834 fn force_repair_item() -> Weight;1835}183618371838pub trait RefungibleExtensionsWeightInfo {1839 1840 fn repartition() -> Weight;1841}184218431844184518461847pub trait CommonCollectionOperations<T: Config> {1848 1849 1850 1851 1852 1853 1854 fn create_item(1855 &self,1856 sender: T::CrossAccountId,1857 to: T::CrossAccountId,1858 data: CreateItemData,1859 nesting_budget: &dyn Budget,1860 ) -> DispatchResultWithPostInfo;18611862 1863 1864 1865 1866 1867 1868 fn create_multiple_items(1869 &self,1870 sender: T::CrossAccountId,1871 to: T::CrossAccountId,1872 data: Vec<CreateItemData>,1873 nesting_budget: &dyn Budget,1874 ) -> DispatchResultWithPostInfo;18751876 1877 1878 1879 1880 1881 1882 fn create_multiple_items_ex(1883 &self,1884 sender: T::CrossAccountId,1885 data: CreateItemExData<T::CrossAccountId>,1886 nesting_budget: &dyn Budget,1887 ) -> DispatchResultWithPostInfo;18881889 1890 1891 1892 1893 1894 fn burn_item(1895 &self,1896 sender: T::CrossAccountId,1897 token: TokenId,1898 amount: u128,1899 ) -> DispatchResultWithPostInfo;19001901 1902 1903 1904 1905 1906 1907 fn burn_item_recursively(1908 &self,1909 sender: T::CrossAccountId,1910 token: TokenId,1911 self_budget: &dyn Budget,1912 breadth_budget: &dyn Budget,1913 ) -> DispatchResultWithPostInfo;19141915 1916 1917 1918 1919 fn set_collection_properties(1920 &self,1921 sender: T::CrossAccountId,1922 properties: Vec<Property>,1923 ) -> DispatchResultWithPostInfo;19241925 1926 1927 1928 1929 fn delete_collection_properties(1930 &self,1931 sender: &T::CrossAccountId,1932 property_keys: Vec<PropertyKey>,1933 ) -> DispatchResultWithPostInfo;19341935 1936 1937 1938 1939 1940 1941 1942 1943 1944 fn set_token_properties(1945 &self,1946 sender: T::CrossAccountId,1947 token_id: TokenId,1948 properties: Vec<Property>,1949 budget: &dyn Budget,1950 ) -> DispatchResultWithPostInfo;19511952 1953 1954 1955 1956 1957 1958 1959 1960 1961 fn delete_token_properties(1962 &self,1963 sender: T::CrossAccountId,1964 token_id: TokenId,1965 property_keys: Vec<PropertyKey>,1966 budget: &dyn Budget,1967 ) -> DispatchResultWithPostInfo;19681969 1970 1971 1972 1973 1974 1975 fn set_token_property_permissions(1976 &self,1977 sender: &T::CrossAccountId,1978 property_permissions: Vec<PropertyKeyPermission>,1979 ) -> DispatchResultWithPostInfo;19801981 1982 1983 1984 1985 1986 1987 1988 fn transfer(1989 &self,1990 sender: T::CrossAccountId,1991 to: T::CrossAccountId,1992 token: TokenId,1993 amount: u128,1994 budget: &dyn Budget,1995 ) -> DispatchResultWithPostInfo;19961997 1998 1999 2000 2001 2002 2003 fn approve(2004 &self,2005 sender: T::CrossAccountId,2006 spender: T::CrossAccountId,2007 token: TokenId,2008 amount: u128,2009 ) -> DispatchResultWithPostInfo;20102011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 fn transfer_from(2022 &self,2023 sender: T::CrossAccountId,2024 from: T::CrossAccountId,2025 to: T::CrossAccountId,2026 token: TokenId,2027 amount: u128,2028 budget: &dyn Budget,2029 ) -> DispatchResultWithPostInfo;20302031 2032 2033 2034 2035 2036 2037 2038 2039 2040 fn burn_from(2041 &self,2042 sender: T::CrossAccountId,2043 from: T::CrossAccountId,2044 token: TokenId,2045 amount: u128,2046 budget: &dyn Budget,2047 ) -> DispatchResultWithPostInfo;20482049 2050 2051 2052 2053 2054 2055 fn check_nesting(2056 &self,2057 sender: T::CrossAccountId,2058 from: (CollectionId, TokenId),2059 under: TokenId,2060 budget: &dyn Budget,2061 ) -> DispatchResult;20622063 2064 2065 2066 2067 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20682069 2070 2071 2072 2073 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20742075 2076 2077 2078 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;20792080 2081 fn collection_tokens(&self) -> Vec<TokenId>;20822083 2084 2085 2086 fn token_exists(&self, token: TokenId) -> bool;20872088 2089 fn last_token_id(&self) -> TokenId;20902091 2092 2093 2094 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;20952096 2097 2098 2099 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;21002101 2102 2103 2104 2105 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21062107 2108 2109 2110 2111 2112 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21132114 2115 fn total_supply(&self) -> u32;21162117 2118 2119 2120 fn account_balance(&self, account: T::CrossAccountId) -> u32;21212122 2123 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21242125 2126 fn total_pieces(&self, token: TokenId) -> Option<u128>;21272128 2129 2130 2131 2132 2133 fn allowance(2134 &self,2135 sender: T::CrossAccountId,2136 spender: T::CrossAccountId,2137 token: TokenId,2138 ) -> u128;21392140 2141 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21422143 2144 2145 2146 2147 fn set_allowance_for_all(2148 &self,2149 owner: T::CrossAccountId,2150 operator: T::CrossAccountId,2151 approve: bool,2152 ) -> DispatchResultWithPostInfo;21532154 2155 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21562157 2158 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2159}216021612162pub trait RefungibleExtensions<T>2163where2164 T: Config,2165{2166 2167 2168 2169 2170 2171 2172 2173 fn repartition(2174 &self,2175 sender: &T::CrossAccountId,2176 token: TokenId,2177 amount: u128,2178 ) -> DispatchResultWithPostInfo;2179}21802181218221832184pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2185 let post_info = PostDispatchInfo {2186 actual_weight: Some(weight),2187 pays_fee: Pays::Yes,2188 };2189 match res {2190 Ok(()) => Ok(post_info),2191 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2192 }2193}21942195impl<T: Config> From<PropertiesError> for Error<T> {2196 fn from(error: PropertiesError) -> Self {2197 match error {2198 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2199 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2200 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2201 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2202 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2203 }2204 }2205}