12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#![warn(missing_docs)]53#![cfg_attr(not(feature = "std"), no_std)]54extern crate alloc;5556use core::ops::{Deref, DerefMut};57use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};58use sp_std::vec::Vec;59use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};60use evm_coder::ToLog;61use frame_support::{62 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},63 ensure,64 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},65 dispatch::Pays,66 transactional,67};68use pallet_evm::GasWeightMapping;69use up_data_structs::{70 COLLECTION_NUMBER_LIMIT,71 Collection,72 RpcCollection,73 CollectionFlags,74 RpcCollectionFlags,75 CollectionId,76 CreateItemData,77 MAX_TOKEN_PREFIX_LENGTH,78 COLLECTION_ADMINS_LIMIT,79 TokenId,80 TokenChild,81 CollectionStats,82 MAX_TOKEN_OWNERSHIP,83 CollectionMode,84 NFT_SPONSOR_TRANSFER_TIMEOUT,85 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,86 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,87 MAX_SPONSOR_TIMEOUT,88 CUSTOM_DATA_LIMIT,89 CollectionLimits,90 CreateCollectionData,91 SponsorshipState,92 CreateItemExData,93 SponsoringRateLimit,94 budget::Budget,95 PhantomType,96 Property,97 Properties,98 PropertiesPermissionMap,99 PropertyKey,100 PropertyValue,101 PropertyPermission,102 PropertiesError,103 PropertyKeyPermission,104 TokenData,105 TrySetProperty,106 PropertyScope,107 108 RmrkCollectionInfo,109 RmrkInstanceInfo,110 RmrkResourceInfo,111 RmrkPropertyInfo,112 RmrkBaseInfo,113 RmrkPartType,114 RmrkBoundedTheme,115 RmrkNftChild,116 CollectionPermissions,117};118use up_pov_estimate_rpc::PovInfo;119120pub use pallet::*;121use sp_core::H160;122use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};123#[cfg(feature = "runtime-benchmarks")]124pub mod benchmarking;125pub mod dispatch;126pub mod erc;127pub mod eth;128pub mod weights;129130131pub type SelfWeightOf<T> = <T as Config>::WeightInfo;132133134135136137138139#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]140pub struct CollectionHandle<T: Config> {141 142 pub id: CollectionId,143 collection: Collection<T::AccountId>,144 145 pub recorder: SubstrateRecorder<T>,146}147148impl<T: Config> WithRecorder<T> for CollectionHandle<T> {149 fn recorder(&self) -> &SubstrateRecorder<T> {150 &self.recorder151 }152 fn into_recorder(self) -> SubstrateRecorder<T> {153 self.recorder154 }155}156157impl<T: Config> CollectionHandle<T> {158 159 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {160 <CollectionById<T>>::get(id).map(|collection| Self {161 id,162 collection,163 recorder: SubstrateRecorder::new(gas_limit),164 })165 }166167 168 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {169 <CollectionById<T>>::get(id).map(|collection| Self {170 id,171 collection,172 recorder,173 })174 }175176 177 178 pub fn new(id: CollectionId) -> Option<Self> {179 Self::new_with_gas_limit(id, u64::MAX)180 }181182 183 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {184 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)185 }186187 188 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {189 self.recorder190 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(191 <T as frame_system::Config>::DbWeight::get()192 .read193 .saturating_mul(reads),194 )))195 }196197 198 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {199 self.recorder200 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(201 <T as frame_system::Config>::DbWeight::get()202 .write203 .saturating_mul(writes),204 )))205 }206207 208 pub fn consume_store_reads_and_writes(209 &self,210 reads: u64,211 writes: u64,212 ) -> evm_coder::execution::Result<()> {213 let weight = <T as frame_system::Config>::DbWeight::get();214 let reads = weight.read.saturating_mul(reads);215 let writes = weight.read.saturating_mul(writes);216 self.recorder217 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(218 reads.saturating_add(writes),219 )))220 }221222 223 pub fn save(&self) -> DispatchResult {224 <CollectionById<T>>::insert(self.id, &self.collection);225 Ok(())226 }227228 229 230 231 232 233 pub fn set_sponsor(234 &mut self,235 sender: &T::CrossAccountId,236 sponsor: T::AccountId,237 ) -> DispatchResult {238 self.check_is_internal()?;239 self.check_is_owner_or_admin(sender)?;240241 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());242243 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));244 <PalletEvm<T>>::deposit_log(245 erc::CollectionHelpersEvents::CollectionChanged {246 collection_id: eth::collection_id_to_address(self.id),247 }248 .to_log(T::ContractAddress::get()),249 );250251 self.save()252 }253254 255 256 257 258 259 260 261 262 263 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {264 self.check_is_internal()?;265266 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());267268 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));269 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));270 <PalletEvm<T>>::deposit_log(271 erc::CollectionHelpersEvents::CollectionChanged {272 collection_id: eth::collection_id_to_address(self.id),273 }274 .to_log(T::ContractAddress::get()),275 );276277 self.save()278 }279280 281 282 283 284 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {285 self.check_is_internal()?;286 ensure!(287 self.collection.sponsorship.pending_sponsor() == Some(sender),288 Error::<T>::ConfirmSponsorshipFail289 );290291 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());292293 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));294 <PalletEvm<T>>::deposit_log(295 erc::CollectionHelpersEvents::CollectionChanged {296 collection_id: eth::collection_id_to_address(self.id),297 }298 .to_log(T::ContractAddress::get()),299 );300301 self.save()302 }303304 305 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {306 self.check_is_internal()?;307 self.check_is_owner_or_admin(sender)?;308309 self.collection.sponsorship = SponsorshipState::Disabled;310311 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));312 <PalletEvm<T>>::deposit_log(313 erc::CollectionHelpersEvents::CollectionChanged {314 collection_id: eth::collection_id_to_address(self.id),315 }316 .to_log(T::ContractAddress::get()),317 );318 self.save()319 }320321 322 323 324 325 pub fn force_remove_sponsor(&mut self) -> DispatchResult {326 self.check_is_internal()?;327328 self.collection.sponsorship = SponsorshipState::Disabled;329330 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));331 <PalletEvm<T>>::deposit_log(332 erc::CollectionHelpersEvents::CollectionChanged {333 collection_id: eth::collection_id_to_address(self.id),334 }335 .to_log(T::ContractAddress::get()),336 );337 self.save()338 }339340 341 342 pub fn check_is_internal(&self) -> DispatchResult {343 if self.flags.external {344 return Err(<Error<T>>::CollectionIsExternal)?;345 }346347 Ok(())348 }349350 351 352 pub fn check_is_external(&self) -> DispatchResult {353 if !self.flags.external {354 return Err(<Error<T>>::CollectionIsInternal)?;355 }356357 Ok(())358 }359}360361impl<T: Config> Deref for CollectionHandle<T> {362 type Target = Collection<T::AccountId>;363364 fn deref(&self) -> &Self::Target {365 &self.collection366 }367}368369impl<T: Config> DerefMut for CollectionHandle<T> {370 fn deref_mut(&mut self) -> &mut Self::Target {371 &mut self.collection372 }373}374375impl<T: Config> CollectionHandle<T> {376 377 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {378 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);379 Ok(())380 }381382 383 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {384 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))385 }386387 388 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {389 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);390 Ok(())391 }392393 394 pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {395 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)396 }397398 399 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {400 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)401 }402403 404 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {405 ensure!(406 <Allowlist<T>>::get((self.id, user)),407 <Error<T>>::AddressNotInAllowlist408 );409 Ok(())410 }411412 413 414 415 pub fn change_owner(416 &mut self,417 caller: T::CrossAccountId,418 new_owner: T::CrossAccountId,419 ) -> DispatchResult {420 self.check_is_internal()?;421 self.check_is_owner(&caller)?;422 self.collection.owner = new_owner.as_sub().clone();423424 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(425 self.id,426 new_owner.as_sub().clone(),427 ));428 <PalletEvm<T>>::deposit_log(429 erc::CollectionHelpersEvents::CollectionChanged {430 collection_id: eth::collection_id_to_address(self.id),431 }432 .to_log(T::ContractAddress::get()),433 );434435 self.save()436 }437}438439#[frame_support::pallet]440pub mod pallet {441 use super::*;442 use dispatch::CollectionDispatch;443 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};444 use frame_system::pallet_prelude::*;445 use frame_support::traits::Currency;446 use up_data_structs::{TokenId, mapping::TokenAddressMapping};447 use scale_info::TypeInfo;448 use weights::WeightInfo;449450 #[pallet::config]451 pub trait Config:452 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo453 {454 455 type WeightInfo: WeightInfo;456457 458 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;459460 461 type Currency: Currency<Self::AccountId>;462463 464 #[pallet::constant]465 type CollectionCreationPrice: Get<466 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,467 >;468469 470 type CollectionDispatch: CollectionDispatch<Self>;471472 473 type TreasuryAccountId: Get<Self::AccountId>;474475 476 #[pallet::constant]477 type ContractAddress: Get<H160>;478479 480 type EvmTokenAddressMapping: TokenAddressMapping<H160>;481482 483 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;484 }485486 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);487488 #[pallet::pallet]489 #[pallet::storage_version(STORAGE_VERSION)]490 #[pallet::generate_store(pub(super) trait Store)]491 pub struct Pallet<T>(_);492493 #[pallet::extra_constants]494 impl<T: Config> Pallet<T> {495 496 pub fn collection_admins_limit() -> u32 {497 COLLECTION_ADMINS_LIMIT498 }499 }500501 #[pallet::event]502 #[pallet::generate_deposit(pub fn deposit_event)]503 pub enum Event<T: Config> {504 505 CollectionCreated(506 507 CollectionId,508 509 u8,510 511 T::AccountId,512 ),513514 515 CollectionDestroyed(516 517 CollectionId,518 ),519520 521 ItemCreated(522 523 CollectionId,524 525 TokenId,526 527 T::CrossAccountId,528 529 u128,530 ),531532 533 ItemDestroyed(534 535 CollectionId,536 537 TokenId,538 539 T::CrossAccountId,540 541 u128,542 ),543544 545 Transfer(546 547 CollectionId,548 549 TokenId,550 551 T::CrossAccountId,552 553 T::CrossAccountId,554 555 u128,556 ),557558 559 Approved(560 561 CollectionId,562 563 TokenId,564 565 T::CrossAccountId,566 567 T::CrossAccountId,568 569 u128,570 ),571572 573 ApprovedForAll(574 575 CollectionId,576 577 T::CrossAccountId,578 579 T::CrossAccountId,580 581 bool,582 ),583584 585 CollectionPropertySet(586 587 CollectionId,588 589 PropertyKey,590 ),591592 593 CollectionPropertyDeleted(594 595 CollectionId,596 597 PropertyKey,598 ),599600 601 TokenPropertySet(602 603 CollectionId,604 605 TokenId,606 607 PropertyKey,608 ),609610 611 TokenPropertyDeleted(612 613 CollectionId,614 615 TokenId,616 617 PropertyKey,618 ),619620 621 PropertyPermissionSet(622 623 CollectionId,624 625 PropertyKey,626 ),627628 629 AllowListAddressAdded(630 631 CollectionId,632 633 T::CrossAccountId,634 ),635636 637 AllowListAddressRemoved(638 639 CollectionId,640 641 T::CrossAccountId,642 ),643644 645 CollectionAdminAdded(646 647 CollectionId,648 649 T::CrossAccountId,650 ),651652 653 CollectionAdminRemoved(654 655 CollectionId,656 657 T::CrossAccountId,658 ),659660 661 CollectionLimitSet(662 663 CollectionId,664 ),665666 667 CollectionOwnerChanged(668 669 CollectionId,670 671 T::AccountId,672 ),673674 675 CollectionPermissionSet(676 677 CollectionId,678 ),679680 681 CollectionSponsorSet(682 683 CollectionId,684 685 T::AccountId,686 ),687688 689 SponsorshipConfirmed(690 691 CollectionId,692 693 T::AccountId,694 ),695696 697 CollectionSponsorRemoved(698 699 CollectionId,700 ),701 }702703 #[pallet::error]704 pub enum Error<T> {705 706 CollectionNotFound,707 708 MustBeTokenOwner,709 710 NoPermission,711 712 CantDestroyNotEmptyCollection,713 714 PublicMintingNotAllowed,715 716 AddressNotInAllowlist,717718 719 CollectionNameLimitExceeded,720 721 CollectionDescriptionLimitExceeded,722 723 CollectionTokenPrefixLimitExceeded,724 725 TotalCollectionsLimitExceeded,726 727 CollectionAdminCountExceeded,728 729 CollectionLimitBoundsExceeded,730 731 OwnerPermissionsCantBeReverted,732 733 TransferNotAllowed,734 735 AccountTokenLimitExceeded,736 737 CollectionTokenLimitExceeded,738 739 MetadataFlagFrozen,740741 742 TokenNotFound,743 744 TokenValueTooLow,745 746 ApprovedValueTooLow,747 748 CantApproveMoreThanOwned,749 750 AddressIsNotEthMirror,751752 753 AddressIsZero,754755 756 UnsupportedOperation,757758 759 NotSufficientFounds,760761 762 UserIsNotAllowedToNest,763 764 SourceCollectionIsNotAllowedToNest,765766 767 CollectionFieldSizeExceeded,768769 770 NoSpaceForProperty,771772 773 PropertyLimitReached,774775 776 PropertyKeyIsTooLong,777778 779 InvalidCharacterInPropertyKey,780781 782 EmptyPropertyKey,783784 785 CollectionIsExternal,786787 788 CollectionIsInternal,789790 791 ConfirmSponsorshipFail,792793 794 UserIsNotCollectionAdmin,795 }796797 798 #[pallet::storage]799 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;800801 802 #[pallet::storage]803 pub type DestroyedCollectionCount<T> =804 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;805806 807 #[pallet::storage]808 pub type CollectionById<T> = StorageMap<809 Hasher = Blake2_128Concat,810 Key = CollectionId,811 Value = Collection<<T as frame_system::Config>::AccountId>,812 QueryKind = OptionQuery,813 >;814815 816 #[pallet::storage]817 #[pallet::getter(fn collection_properties)]818 pub type CollectionProperties<T> = StorageMap<819 Hasher = Blake2_128Concat,820 Key = CollectionId,821 Value = Properties,822 QueryKind = ValueQuery,823 OnEmpty = up_data_structs::CollectionProperties,824 >;825826 827 #[pallet::storage]828 #[pallet::getter(fn property_permissions)]829 pub type CollectionPropertyPermissions<T> = StorageMap<830 Hasher = Blake2_128Concat,831 Key = CollectionId,832 Value = PropertiesPermissionMap,833 QueryKind = ValueQuery,834 >;835836 837 #[pallet::storage]838 pub type AdminAmount<T> = StorageMap<839 Hasher = Blake2_128Concat,840 Key = CollectionId,841 Value = u32,842 QueryKind = ValueQuery,843 >;844845 846 #[pallet::storage]847 pub type IsAdmin<T: Config> = StorageNMap<848 Key = (849 Key<Blake2_128Concat, CollectionId>,850 Key<Blake2_128Concat, T::CrossAccountId>,851 ),852 Value = bool,853 QueryKind = ValueQuery,854 >;855856 857 #[pallet::storage]858 pub type Allowlist<T: Config> = StorageNMap<859 Key = (860 Key<Blake2_128Concat, CollectionId>,861 Key<Blake2_128Concat, T::CrossAccountId>,862 ),863 Value = bool,864 QueryKind = ValueQuery,865 >;866867 868 #[pallet::storage]869 pub type DummyStorageValue<T: Config> = StorageValue<870 Value = (871 CollectionStats,872 CollectionId,873 TokenId,874 TokenChild,875 PhantomType<(876 TokenData<T::CrossAccountId>,877 RpcCollection<T::AccountId>,878 879 RmrkCollectionInfo<T::AccountId>,880 RmrkInstanceInfo<T::AccountId>,881 RmrkResourceInfo,882 RmrkPropertyInfo,883 RmrkBaseInfo<T::AccountId>,884 RmrkPartType,885 RmrkBoundedTheme,886 RmrkNftChild,887 888 PovInfo,889 )>,890 ),891 QueryKind = OptionQuery,892 >;893894 #[pallet::hooks]895 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {896 fn on_runtime_upgrade() -> Weight {897 StorageVersion::new(1).put::<Pallet<T>>();898899 Weight::zero()900 }901 }902}903904impl<T: Config> Pallet<T> {905 906 907 908 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {909 ensure!(910 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,911 <Error<T>>::AddressIsZero912 );913 Ok(())914 }915916 917 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {918 <IsAdmin<T>>::iter_prefix((collection,))919 .map(|(a, _)| a)920 .collect()921 }922923 924 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {925 <Allowlist<T>>::iter_prefix((collection,))926 .map(|(a, _)| a)927 .collect()928 }929930 931 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {932 <Allowlist<T>>::get((collection, user))933 }934935 936 pub fn collection_stats() -> CollectionStats {937 let created = <CreatedCollectionCount<T>>::get();938 let destroyed = <DestroyedCollectionCount<T>>::get();939 CollectionStats {940 created: created.0,941 destroyed: destroyed.0,942 alive: created.0 - destroyed.0,943 }944 }945946 947 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {948 let collection = <CollectionById<T>>::get(collection)?;949 let limits = collection.limits;950 let effective_limits = CollectionLimits {951 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),952 sponsored_data_size: Some(limits.sponsored_data_size()),953 sponsored_data_rate_limit: Some(954 limits955 .sponsored_data_rate_limit956 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),957 ),958 token_limit: Some(limits.token_limit()),959 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(960 match collection.mode {961 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,962 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,963 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,964 },965 )),966 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),967 owner_can_transfer: Some(limits.owner_can_transfer()),968 owner_can_destroy: Some(limits.owner_can_destroy()),969 transfers_enabled: Some(limits.transfers_enabled()),970 };971972 Some(effective_limits)973 }974975 976 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {977 let Collection {978 name,979 description,980 owner,981 mode,982 token_prefix,983 sponsorship,984 limits,985 permissions,986 flags,987 } = <CollectionById<T>>::get(collection)?;988989 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)990 .into_iter()991 .map(|(key, permission)| PropertyKeyPermission { key, permission })992 .collect();993994 let properties = <CollectionProperties<T>>::get(collection)995 .into_iter()996 .map(|(key, value)| Property { key, value })997 .collect();998999 let permissions = CollectionPermissions {1000 access: Some(permissions.access()),1001 mint_mode: Some(permissions.mint_mode()),1002 nesting: Some(permissions.nesting().clone()),1003 };10041005 Some(RpcCollection {1006 name: name.into_inner(),1007 description: description.into_inner(),1008 owner,1009 mode,1010 token_prefix: token_prefix.into_inner(),1011 sponsorship,1012 limits,1013 permissions,1014 token_property_permissions,1015 properties,1016 read_only: flags.external,10171018 flags: RpcCollectionFlags {1019 foreign: flags.foreign,1020 erc721metadata: flags.erc721metadata,1021 },1022 })1023 }1024}10251026macro_rules! limit_default {1027 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1028 $(1029 if let Some($new) = $new.$field {1030 let $old = $old.$field($($arg)?);1031 let _ = $new;1032 let _ = $old;1033 $check1034 } else {1035 $new.$field = $old.$field1036 }1037 )*1038 }};1039}1040macro_rules! limit_default_clone {1041 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1042 $(1043 if let Some($new) = $new.$field.clone() {1044 let $old = $old.$field($($arg)?);1045 let _ = $new;1046 let _ = $old;1047 $check1048 } else {1049 $new.$field = $old.$field.clone()1050 }1051 )*1052 }};1053}10541055impl<T: Config> Pallet<T> {1056 1057 1058 1059 1060 1061 pub fn init_collection(1062 owner: T::CrossAccountId,1063 payer: T::CrossAccountId,1064 data: CreateCollectionData<T::AccountId>,1065 flags: CollectionFlags,1066 ) -> Result<CollectionId, DispatchError> {1067 {1068 ensure!(1069 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1070 Error::<T>::CollectionTokenPrefixLimitExceeded1071 );1072 }10731074 let created_count = <CreatedCollectionCount<T>>::get()1075 .01076 .checked_add(1)1077 .ok_or(ArithmeticError::Overflow)?;1078 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1079 let id = CollectionId(created_count);10801081 1082 ensure!(1083 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1084 <Error<T>>::TotalCollectionsLimitExceeded1085 );10861087 10881089 let collection = Collection {1090 owner: owner.as_sub().clone(),1091 name: data.name,1092 mode: data.mode.clone(),1093 description: data.description,1094 token_prefix: data.token_prefix,1095 sponsorship: data1096 .pending_sponsor1097 .map(SponsorshipState::Unconfirmed)1098 .unwrap_or_default(),1099 limits: data1100 .limits1101 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1102 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1103 permissions: data1104 .permissions1105 .map(|permissions| {1106 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1107 })1108 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1109 flags,1110 };11111112 let mut collection_properties = up_data_structs::CollectionProperties::get();1113 collection_properties1114 .try_set_from_iter(data.properties.into_iter())1115 .map_err(<Error<T>>::from)?;11161117 CollectionProperties::<T>::insert(id, collection_properties);11181119 let mut token_props_permissions = PropertiesPermissionMap::new();1120 token_props_permissions1121 .try_set_from_iter(data.token_property_permissions.into_iter())1122 .map_err(<Error<T>>::from)?;11231124 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11251126 1127 {1128 let mut imbalance =1129 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1130 imbalance.subsume(1131 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1132 &T::TreasuryAccountId::get(),1133 T::CollectionCreationPrice::get(),1134 ),1135 );1136 <T as Config>::Currency::settle(1137 payer.as_sub(),1138 imbalance,1139 WithdrawReasons::TRANSFER,1140 ExistenceRequirement::KeepAlive,1141 )1142 .map_err(|_| Error::<T>::NotSufficientFounds)?;1143 }11441145 <CreatedCollectionCount<T>>::put(created_count);1146 <Pallet<T>>::deposit_event(Event::CollectionCreated(1147 id,1148 data.mode.id(),1149 owner.as_sub().clone(),1150 ));1151 <PalletEvm<T>>::deposit_log(1152 erc::CollectionHelpersEvents::CollectionCreated {1153 owner: *owner.as_eth(),1154 collection_id: eth::collection_id_to_address(id),1155 }1156 .to_log(T::ContractAddress::get()),1157 );1158 <CollectionById<T>>::insert(id, collection);1159 Ok(id)1160 }11611162 1163 1164 1165 1166 pub fn destroy_collection(1167 collection: CollectionHandle<T>,1168 sender: &T::CrossAccountId,1169 ) -> DispatchResult {1170 ensure!(1171 collection.limits.owner_can_destroy(),1172 <Error<T>>::NoPermission,1173 );1174 collection.check_is_owner(sender)?;11751176 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1177 .01178 .checked_add(1)1179 .ok_or(ArithmeticError::Overflow)?;11801181 11821183 <DestroyedCollectionCount<T>>::put(destroyed_collections);1184 <CollectionById<T>>::remove(collection.id);1185 <AdminAmount<T>>::remove(collection.id);1186 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1187 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1188 <CollectionProperties<T>>::remove(collection.id);11891190 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11911192 <PalletEvm<T>>::deposit_log(1193 erc::CollectionHelpersEvents::CollectionDestroyed {1194 collection_id: eth::collection_id_to_address(collection.id),1195 }1196 .to_log(T::ContractAddress::get()),1197 );1198 Ok(())1199 }12001201 1202 1203 1204 1205 1206 pub fn set_collection_property(1207 collection: &CollectionHandle<T>,1208 sender: &T::CrossAccountId,1209 property: Property,1210 ) -> DispatchResult {1211 collection.check_is_owner_or_admin(sender)?;12121213 CollectionProperties::<T>::try_mutate(collection.id, |properties| {1214 let property = property.clone();1215 properties.try_set(property.key, property.value)1216 })1217 .map_err(<Error<T>>::from)?;12181219 Self::deposit_event(Event::CollectionPropertySet(collection.id, property.key));1220 <PalletEvm<T>>::deposit_log(1221 erc::CollectionHelpersEvents::CollectionChanged {1222 collection_id: eth::collection_id_to_address(collection.id),1223 }1224 .to_log(T::ContractAddress::get()),1225 );12261227 Ok(())1228 }12291230 1231 1232 1233 1234 1235 1236 pub fn set_scoped_collection_property(1237 collection_id: CollectionId,1238 scope: PropertyScope,1239 property: Property,1240 ) -> DispatchResult {1241 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1242 properties.try_scoped_set(scope, property.key, property.value)1243 })1244 .map_err(<Error<T>>::from)?;12451246 Ok(())1247 }12481249 1250 1251 1252 1253 1254 1255 pub fn set_scoped_collection_properties(1256 collection_id: CollectionId,1257 scope: PropertyScope,1258 properties: impl Iterator<Item = Property>,1259 ) -> DispatchResult {1260 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1261 stored_properties.try_scoped_set_from_iter(scope, properties)1262 })1263 .map_err(<Error<T>>::from)?;12641265 Ok(())1266 }12671268 1269 1270 1271 1272 1273 #[transactional]1274 pub fn set_collection_properties(1275 collection: &CollectionHandle<T>,1276 sender: &T::CrossAccountId,1277 properties: Vec<Property>,1278 ) -> DispatchResult {1279 for property in properties {1280 Self::set_collection_property(collection, sender, property)?;1281 }12821283 Ok(())1284 }12851286 1287 1288 1289 1290 1291 pub fn delete_collection_property(1292 collection: &CollectionHandle<T>,1293 sender: &T::CrossAccountId,1294 property_key: PropertyKey,1295 ) -> DispatchResult {1296 collection.check_is_owner_or_admin(sender)?;12971298 CollectionProperties::<T>::try_mutate(collection.id, |properties| {1299 properties.remove(&property_key)1300 })1301 .map_err(<Error<T>>::from)?;13021303 Self::deposit_event(Event::CollectionPropertyDeleted(1304 collection.id,1305 property_key,1306 ));1307 <PalletEvm<T>>::deposit_log(1308 erc::CollectionHelpersEvents::CollectionChanged {1309 collection_id: eth::collection_id_to_address(collection.id),1310 }1311 .to_log(T::ContractAddress::get()),1312 );13131314 Ok(())1315 }13161317 1318 1319 1320 1321 1322 #[transactional]1323 pub fn delete_collection_properties(1324 collection: &CollectionHandle<T>,1325 sender: &T::CrossAccountId,1326 property_keys: Vec<PropertyKey>,1327 ) -> DispatchResult {1328 for key in property_keys {1329 Self::delete_collection_property(collection, sender, key)?;1330 }13311332 Ok(())1333 }13341335 1336 1337 1338 1339 1340 1341 pub fn set_property_permission_unchecked(1342 collection: CollectionId,1343 property_permission: PropertyKeyPermission,1344 ) -> DispatchResult {1345 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1346 permissions.try_set(property_permission.key, property_permission.permission)1347 })1348 .map_err(<Error<T>>::from)?;1349 Ok(())1350 }13511352 1353 1354 1355 1356 1357 pub fn set_property_permission(1358 collection: &CollectionHandle<T>,1359 sender: &T::CrossAccountId,1360 property_permission: PropertyKeyPermission,1361 ) -> DispatchResult {1362 Self::set_scoped_property_permission(1363 collection,1364 sender,1365 PropertyScope::None,1366 property_permission,1367 )1368 }13691370 1371 1372 1373 1374 1375 1376 pub fn set_scoped_property_permission(1377 collection: &CollectionHandle<T>,1378 sender: &T::CrossAccountId,1379 scope: PropertyScope,1380 property_permission: PropertyKeyPermission,1381 ) -> DispatchResult {1382 collection.check_is_owner_or_admin(sender)?;13831384 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1385 let current_permission = all_permissions.get(&property_permission.key);1386 if matches![1387 current_permission,1388 Some(PropertyPermission { mutable: false, .. })1389 ] {1390 return Err(<Error<T>>::NoPermission.into());1391 }13921393 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1394 let property_permission = property_permission.clone();1395 permissions.try_scoped_set(1396 scope,1397 property_permission.key,1398 property_permission.permission,1399 )1400 })1401 .map_err(<Error<T>>::from)?;14021403 Self::deposit_event(Event::PropertyPermissionSet(1404 collection.id,1405 property_permission.key,1406 ));1407 <PalletEvm<T>>::deposit_log(1408 erc::CollectionHelpersEvents::CollectionChanged {1409 collection_id: eth::collection_id_to_address(collection.id),1410 }1411 .to_log(T::ContractAddress::get()),1412 );14131414 Ok(())1415 }14161417 1418 1419 1420 1421 1422 #[transactional]1423 pub fn set_token_property_permissions(1424 collection: &CollectionHandle<T>,1425 sender: &T::CrossAccountId,1426 property_permissions: Vec<PropertyKeyPermission>,1427 ) -> DispatchResult {1428 Self::set_scoped_token_property_permissions(1429 collection,1430 sender,1431 PropertyScope::None,1432 property_permissions,1433 )1434 }14351436 1437 1438 1439 1440 1441 1442 #[transactional]1443 pub fn set_scoped_token_property_permissions(1444 collection: &CollectionHandle<T>,1445 sender: &T::CrossAccountId,1446 scope: PropertyScope,1447 property_permissions: Vec<PropertyKeyPermission>,1448 ) -> DispatchResult {1449 for prop_pemission in property_permissions {1450 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1451 }14521453 Ok(())1454 }14551456 1457 pub fn get_collection_property(1458 collection_id: CollectionId,1459 key: &PropertyKey,1460 ) -> Option<PropertyValue> {1461 Self::collection_properties(collection_id).get(key).cloned()1462 }14631464 1465 pub fn bytes_keys_to_property_keys(1466 keys: Vec<Vec<u8>>,1467 ) -> Result<Vec<PropertyKey>, DispatchError> {1468 keys.into_iter()1469 .map(|key| -> Result<PropertyKey, DispatchError> {1470 key.try_into()1471 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1472 })1473 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1474 }14751476 1477 pub fn filter_collection_properties(1478 collection_id: CollectionId,1479 keys: Option<Vec<PropertyKey>>,1480 ) -> Result<Vec<Property>, DispatchError> {1481 let properties = Self::collection_properties(collection_id);14821483 let properties = keys1484 .map(|keys| {1485 keys.into_iter()1486 .filter_map(|key| {1487 properties.get(&key).map(|value| Property {1488 key,1489 value: value.clone(),1490 })1491 })1492 .collect()1493 })1494 .unwrap_or_else(|| {1495 properties1496 .into_iter()1497 .map(|(key, value)| Property { key, value })1498 .collect()1499 });15001501 Ok(properties)1502 }15031504 1505 pub fn filter_property_permissions(1506 collection_id: CollectionId,1507 keys: Option<Vec<PropertyKey>>,1508 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1509 let permissions = Self::property_permissions(collection_id);15101511 let key_permissions = keys1512 .map(|keys| {1513 keys.into_iter()1514 .filter_map(|key| {1515 permissions1516 .get(&key)1517 .map(|permission| PropertyKeyPermission {1518 key,1519 permission: permission.clone(),1520 })1521 })1522 .collect()1523 })1524 .unwrap_or_else(|| {1525 permissions1526 .into_iter()1527 .map(|(key, permission)| PropertyKeyPermission { key, permission })1528 .collect()1529 });15301531 Ok(key_permissions)1532 }15331534 1535 1536 1537 pub fn toggle_allowlist(1538 collection: &CollectionHandle<T>,1539 sender: &T::CrossAccountId,1540 user: &T::CrossAccountId,1541 allowed: bool,1542 ) -> DispatchResult {1543 collection.check_is_owner_or_admin(sender)?;15441545 15461547 if allowed {1548 <Allowlist<T>>::insert((collection.id, user), true);1549 Self::deposit_event(Event::<T>::AllowListAddressAdded(1550 collection.id,1551 user.clone(),1552 ));1553 } else {1554 <Allowlist<T>>::remove((collection.id, user));1555 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1556 collection.id,1557 user.clone(),1558 ));1559 }15601561 <PalletEvm<T>>::deposit_log(1562 erc::CollectionHelpersEvents::CollectionChanged {1563 collection_id: eth::collection_id_to_address(collection.id),1564 }1565 .to_log(T::ContractAddress::get()),1566 );15671568 Ok(())1569 }15701571 1572 1573 1574 pub fn toggle_admin(1575 collection: &CollectionHandle<T>,1576 sender: &T::CrossAccountId,1577 user: &T::CrossAccountId,1578 admin: bool,1579 ) -> DispatchResult {1580 collection.check_is_internal()?;1581 collection.check_is_owner(sender)?;15821583 let is_admin = <IsAdmin<T>>::get((collection.id, user));1584 if is_admin == admin {1585 if admin {1586 return Ok(());1587 } else {1588 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1589 }1590 }1591 let amount = <AdminAmount<T>>::get(collection.id);15921593 15941595 if admin {1596 let amount = amount1597 .checked_add(1)1598 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1599 ensure!(1600 amount <= Self::collection_admins_limit(),1601 <Error<T>>::CollectionAdminCountExceeded,1602 );16031604 <AdminAmount<T>>::insert(collection.id, amount);1605 <IsAdmin<T>>::insert((collection.id, user), true);16061607 Self::deposit_event(Event::<T>::CollectionAdminAdded(1608 collection.id,1609 user.clone(),1610 ));1611 } else {1612 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1613 <IsAdmin<T>>::remove((collection.id, user));16141615 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1616 collection.id,1617 user.clone(),1618 ));1619 }16201621 <PalletEvm<T>>::deposit_log(1622 erc::CollectionHelpersEvents::CollectionChanged {1623 collection_id: eth::collection_id_to_address(collection.id),1624 }1625 .to_log(T::ContractAddress::get()),1626 );16271628 Ok(())1629 }16301631 1632 pub fn update_limits(1633 user: &T::CrossAccountId,1634 collection: &mut CollectionHandle<T>,1635 new_limit: CollectionLimits,1636 ) -> DispatchResult {1637 collection.check_is_internal()?;1638 collection.check_is_owner_or_admin(user)?;16391640 collection.limits =1641 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16421643 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1644 <PalletEvm<T>>::deposit_log(1645 erc::CollectionHelpersEvents::CollectionChanged {1646 collection_id: eth::collection_id_to_address(collection.id),1647 }1648 .to_log(T::ContractAddress::get()),1649 );16501651 collection.save()1652 }16531654 1655 fn clamp_limits(1656 mode: CollectionMode,1657 old_limit: &CollectionLimits,1658 mut new_limit: CollectionLimits,1659 ) -> Result<CollectionLimits, DispatchError> {1660 let limits = old_limit;1661 limit_default!(old_limit, new_limit,1662 account_token_ownership_limit => ensure!(1663 new_limit <= MAX_TOKEN_OWNERSHIP,1664 <Error<T>>::CollectionLimitBoundsExceeded,1665 ),1666 sponsored_data_size => ensure!(1667 new_limit <= CUSTOM_DATA_LIMIT,1668 <Error<T>>::CollectionLimitBoundsExceeded,1669 ),16701671 sponsored_data_rate_limit => {},1672 token_limit => ensure!(1673 old_limit >= new_limit && new_limit > 0,1674 <Error<T>>::CollectionTokenLimitExceeded1675 ),16761677 sponsor_transfer_timeout(match mode {1678 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1679 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1680 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1681 }) => ensure!(1682 new_limit <= MAX_SPONSOR_TIMEOUT,1683 <Error<T>>::CollectionLimitBoundsExceeded,1684 ),1685 sponsor_approve_timeout => {},1686 owner_can_transfer => ensure!(1687 !limits.owner_can_transfer_instaled() ||1688 old_limit || !new_limit,1689 <Error<T>>::OwnerPermissionsCantBeReverted,1690 ),1691 owner_can_destroy => ensure!(1692 old_limit || !new_limit,1693 <Error<T>>::OwnerPermissionsCantBeReverted,1694 ),1695 transfers_enabled => {},1696 );1697 Ok(new_limit)1698 }16991700 1701 pub fn update_permissions(1702 user: &T::CrossAccountId,1703 collection: &mut CollectionHandle<T>,1704 new_permission: CollectionPermissions,1705 ) -> DispatchResult {1706 collection.check_is_internal()?;1707 collection.check_is_owner_or_admin(user)?;1708 collection.permissions = Self::clamp_permissions(1709 collection.mode.clone(),1710 &collection.permissions,1711 new_permission,1712 )?;17131714 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1715 <PalletEvm<T>>::deposit_log(1716 erc::CollectionHelpersEvents::CollectionChanged {1717 collection_id: eth::collection_id_to_address(collection.id),1718 }1719 .to_log(T::ContractAddress::get()),1720 );17211722 collection.save()1723 }17241725 1726 fn clamp_permissions(1727 _mode: CollectionMode,1728 old_permission: &CollectionPermissions,1729 mut new_permission: CollectionPermissions,1730 ) -> Result<CollectionPermissions, DispatchError> {1731 limit_default_clone!(old_permission, new_permission,1732 access => {},1733 mint_mode => {},1734 nesting => { },1735 );1736 Ok(new_permission)1737 }17381739 1740 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1741 CollectionProperties::<T>::mutate(collection_id, |properties| {1742 properties.recompute_consumed_space();1743 });17441745 Ok(())1746 }1747}174817491750#[macro_export]1751macro_rules! unsupported {1752 ($runtime:path) => {1753 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1754 };1755}175617571758pub trait CommonWeightInfo<CrossAccountId> {1759 1760 fn create_item() -> Weight;17611762 1763 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17641765 1766 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17671768 1769 fn burn_item() -> Weight;17701771 1772 1773 1774 fn set_collection_properties(amount: u32) -> Weight;17751776 1777 1778 1779 fn delete_collection_properties(amount: u32) -> Weight;17801781 1782 1783 1784 fn set_token_properties(amount: u32) -> Weight;17851786 1787 1788 1789 fn delete_token_properties(amount: u32) -> Weight;17901791 1792 1793 1794 fn set_token_property_permissions(amount: u32) -> Weight;17951796 1797 fn transfer() -> Weight;17981799 1800 fn approve() -> Weight;18011802 1803 fn approve_from() -> Weight;18041805 1806 fn transfer_from() -> Weight;18071808 1809 fn burn_from() -> Weight;18101811 1812 1813 1814 1815 fn burn_recursively_self_raw() -> Weight;18161817 1818 1819 1820 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18211822 1823 1824 1825 1826 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1827 Self::burn_recursively_self_raw()1828 .saturating_mul(max_selfs.max(1) as u64)1829 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1830 }18311832 1833 fn token_owner() -> Weight;18341835 1836 fn set_allowance_for_all() -> Weight;18371838 1839 fn force_repair_item() -> Weight;1840}184118421843pub trait RefungibleExtensionsWeightInfo {1844 1845 fn repartition() -> Weight;1846}184718481849185018511852pub trait CommonCollectionOperations<T: Config> {1853 1854 1855 1856 1857 1858 1859 fn create_item(1860 &self,1861 sender: T::CrossAccountId,1862 to: T::CrossAccountId,1863 data: CreateItemData,1864 nesting_budget: &dyn Budget,1865 ) -> DispatchResultWithPostInfo;18661867 1868 1869 1870 1871 1872 1873 fn create_multiple_items(1874 &self,1875 sender: T::CrossAccountId,1876 to: T::CrossAccountId,1877 data: Vec<CreateItemData>,1878 nesting_budget: &dyn Budget,1879 ) -> DispatchResultWithPostInfo;18801881 1882 1883 1884 1885 1886 1887 fn create_multiple_items_ex(1888 &self,1889 sender: T::CrossAccountId,1890 data: CreateItemExData<T::CrossAccountId>,1891 nesting_budget: &dyn Budget,1892 ) -> DispatchResultWithPostInfo;18931894 1895 1896 1897 1898 1899 fn burn_item(1900 &self,1901 sender: T::CrossAccountId,1902 token: TokenId,1903 amount: u128,1904 ) -> DispatchResultWithPostInfo;19051906 1907 1908 1909 1910 1911 1912 fn burn_item_recursively(1913 &self,1914 sender: T::CrossAccountId,1915 token: TokenId,1916 self_budget: &dyn Budget,1917 breadth_budget: &dyn Budget,1918 ) -> DispatchResultWithPostInfo;19191920 1921 1922 1923 1924 fn set_collection_properties(1925 &self,1926 sender: T::CrossAccountId,1927 properties: Vec<Property>,1928 ) -> DispatchResultWithPostInfo;19291930 1931 1932 1933 1934 fn delete_collection_properties(1935 &self,1936 sender: &T::CrossAccountId,1937 property_keys: Vec<PropertyKey>,1938 ) -> DispatchResultWithPostInfo;19391940 1941 1942 1943 1944 1945 1946 1947 1948 1949 fn set_token_properties(1950 &self,1951 sender: T::CrossAccountId,1952 token_id: TokenId,1953 properties: Vec<Property>,1954 budget: &dyn Budget,1955 ) -> DispatchResultWithPostInfo;19561957 1958 1959 1960 1961 1962 1963 1964 1965 1966 fn delete_token_properties(1967 &self,1968 sender: T::CrossAccountId,1969 token_id: TokenId,1970 property_keys: Vec<PropertyKey>,1971 budget: &dyn Budget,1972 ) -> DispatchResultWithPostInfo;19731974 1975 1976 1977 1978 1979 1980 fn set_token_property_permissions(1981 &self,1982 sender: &T::CrossAccountId,1983 property_permissions: Vec<PropertyKeyPermission>,1984 ) -> DispatchResultWithPostInfo;19851986 1987 1988 1989 1990 1991 1992 1993 fn transfer(1994 &self,1995 sender: T::CrossAccountId,1996 to: T::CrossAccountId,1997 token: TokenId,1998 amount: u128,1999 budget: &dyn Budget,2000 ) -> DispatchResultWithPostInfo;20012002 2003 2004 2005 2006 2007 2008 fn approve(2009 &self,2010 sender: T::CrossAccountId,2011 spender: T::CrossAccountId,2012 token: TokenId,2013 amount: u128,2014 ) -> DispatchResultWithPostInfo;20152016 2017 2018 2019 2020 2021 2022 2023 fn approve_from(2024 &self,2025 sender: T::CrossAccountId,2026 from: T::CrossAccountId,2027 to: T::CrossAccountId,2028 token: TokenId,2029 amount: u128,2030 ) -> DispatchResultWithPostInfo;20312032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 fn transfer_from(2043 &self,2044 sender: T::CrossAccountId,2045 from: T::CrossAccountId,2046 to: T::CrossAccountId,2047 token: TokenId,2048 amount: u128,2049 budget: &dyn Budget,2050 ) -> DispatchResultWithPostInfo;20512052 2053 2054 2055 2056 2057 2058 2059 2060 2061 fn burn_from(2062 &self,2063 sender: T::CrossAccountId,2064 from: T::CrossAccountId,2065 token: TokenId,2066 amount: u128,2067 budget: &dyn Budget,2068 ) -> DispatchResultWithPostInfo;20692070 2071 2072 2073 2074 2075 2076 fn check_nesting(2077 &self,2078 sender: T::CrossAccountId,2079 from: (CollectionId, TokenId),2080 under: TokenId,2081 budget: &dyn Budget,2082 ) -> DispatchResult;20832084 2085 2086 2087 2088 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20892090 2091 2092 2093 2094 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));20952096 2097 2098 2099 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21002101 2102 fn collection_tokens(&self) -> Vec<TokenId>;21032104 2105 2106 2107 fn token_exists(&self, token: TokenId) -> bool;21082109 2110 fn last_token_id(&self) -> TokenId;21112112 2113 2114 2115 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;21162117 2118 2119 2120 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;21212122 2123 2124 2125 2126 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21272128 2129 2130 2131 2132 2133 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21342135 2136 fn total_supply(&self) -> u32;21372138 2139 2140 2141 fn account_balance(&self, account: T::CrossAccountId) -> u32;21422143 2144 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21452146 2147 fn total_pieces(&self, token: TokenId) -> Option<u128>;21482149 2150 2151 2152 2153 2154 fn allowance(2155 &self,2156 sender: T::CrossAccountId,2157 spender: T::CrossAccountId,2158 token: TokenId,2159 ) -> u128;21602161 2162 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21632164 2165 2166 2167 2168 fn set_allowance_for_all(2169 &self,2170 owner: T::CrossAccountId,2171 operator: T::CrossAccountId,2172 approve: bool,2173 ) -> DispatchResultWithPostInfo;21742175 2176 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21772178 2179 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2180}218121822183pub trait RefungibleExtensions<T>2184where2185 T: Config,2186{2187 2188 2189 2190 2191 2192 2193 2194 fn repartition(2195 &self,2196 sender: &T::CrossAccountId,2197 token: TokenId,2198 amount: u128,2199 ) -> DispatchResultWithPostInfo;2200}22012202220322042205pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2206 let post_info = PostDispatchInfo {2207 actual_weight: Some(weight),2208 pays_fee: Pays::Yes,2209 };2210 match res {2211 Ok(()) => Ok(post_info),2212 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2213 }2214}22152216impl<T: Config> From<PropertiesError> for Error<T> {2217 fn from(error: PropertiesError) -> Self {2218 match error {2219 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2220 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2221 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2222 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2223 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2224 }2225 }2226}