12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#![warn(missing_docs)]53#![cfg_attr(not(feature = "std"), no_std)]54extern crate alloc;5556use core::{57 ops::{Deref, DerefMut},58 slice::from_ref,59};60use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};61use sp_std::vec::Vec;62use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};63use evm_coder::ToLog;64use frame_support::{65 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},66 ensure,67 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},68 dispatch::Pays,69 transactional, fail,70};71use pallet_evm::GasWeightMapping;72use up_data_structs::{73 AccessMode, COLLECTION_NUMBER_LIMIT, Collection, RpcCollection, CollectionFlags,74 RpcCollectionFlags, CollectionId, CreateItemData, MAX_TOKEN_PREFIX_LENGTH,75 COLLECTION_ADMINS_LIMIT, TokenId, TokenChild, CollectionStats, MAX_TOKEN_OWNERSHIP,76 CollectionMode, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,77 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,78 CreateCollectionData, SponsorshipState, CreateItemExData, SponsoringRateLimit, budget::Budget,79 PhantomType, Property, CollectionProperties as CollectionPropertiesT, TokenProperties,80 PropertiesPermissionMap, PropertyKey, PropertyValue, PropertyPermission, PropertiesError,81 TokenOwnerError, PropertyKeyPermission, TokenData, TrySetProperty, PropertyScope,82 CollectionPermissions,83};84use up_pov_estimate_rpc::PovInfo;8586pub use pallet::*;87use sp_core::H160;88use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};8990#[cfg(feature = "runtime-benchmarks")]91pub mod benchmarking;92pub mod dispatch;93pub mod erc;94pub mod eth;95pub mod weights;969798pub type SelfWeightOf<T> = <T as Config>::WeightInfo;99100101102103104105106#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]107pub struct CollectionHandle<T: Config> {108 109 pub id: CollectionId,110 collection: Collection<T::AccountId>,111 112 pub recorder: SubstrateRecorder<T>,113}114115impl<T: Config> WithRecorder<T> for CollectionHandle<T> {116 fn recorder(&self) -> &SubstrateRecorder<T> {117 &self.recorder118 }119 fn into_recorder(self) -> SubstrateRecorder<T> {120 self.recorder121 }122}123124impl<T: Config> CollectionHandle<T> {125 126 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {127 <CollectionById<T>>::get(id).map(|collection| Self {128 id,129 collection,130 recorder: SubstrateRecorder::new(gas_limit),131 })132 }133134 135 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {136 <CollectionById<T>>::get(id).map(|collection| Self {137 id,138 collection,139 recorder,140 })141 }142143 144 145 pub fn new(id: CollectionId) -> Option<Self> {146 Self::new_with_gas_limit(id, u64::MAX)147 }148149 150 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {151 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)152 }153154 155 pub fn consume_store_reads(156 &self,157 reads: u64,158 ) -> pallet_evm_coder_substrate::execution::Result<()> {159 self.recorder160 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(161 <T as frame_system::Config>::DbWeight::get()162 .read163 .saturating_mul(reads),164 )))165 }166167 168 pub fn consume_store_writes(169 &self,170 writes: u64,171 ) -> pallet_evm_coder_substrate::execution::Result<()> {172 self.recorder173 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(174 <T as frame_system::Config>::DbWeight::get()175 .write176 .saturating_mul(writes),177 )))178 }179180 181 pub fn consume_store_reads_and_writes(182 &self,183 reads: u64,184 writes: u64,185 ) -> pallet_evm_coder_substrate::execution::Result<()> {186 let weight = <T as frame_system::Config>::DbWeight::get();187 let reads = weight.read.saturating_mul(reads);188 let writes = weight.read.saturating_mul(writes);189 self.recorder190 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(191 reads.saturating_add(writes),192 )))193 }194195 196 pub fn save(&self) -> DispatchResult {197 <CollectionById<T>>::insert(self.id, &self.collection);198 Ok(())199 }200201 202 203 204 205 206 pub fn set_sponsor(207 &mut self,208 sender: &T::CrossAccountId,209 sponsor: T::AccountId,210 ) -> DispatchResult {211 self.check_is_internal()?;212 self.check_is_owner_or_admin(sender)?;213214 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());215216 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));217 <PalletEvm<T>>::deposit_log(218 erc::CollectionHelpersEvents::CollectionChanged {219 collection_id: eth::collection_id_to_address(self.id),220 }221 .to_log(T::ContractAddress::get()),222 );223224 self.save()225 }226227 228 229 230 231 232 233 234 235 236 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {237 self.check_is_internal()?;238239 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());240241 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));242 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));243 <PalletEvm<T>>::deposit_log(244 erc::CollectionHelpersEvents::CollectionChanged {245 collection_id: eth::collection_id_to_address(self.id),246 }247 .to_log(T::ContractAddress::get()),248 );249250 self.save()251 }252253 254 255 256 257 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {258 self.check_is_internal()?;259 ensure!(260 self.collection.sponsorship.pending_sponsor() == Some(sender),261 Error::<T>::ConfirmSponsorshipFail262 );263264 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());265266 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));267 <PalletEvm<T>>::deposit_log(268 erc::CollectionHelpersEvents::CollectionChanged {269 collection_id: eth::collection_id_to_address(self.id),270 }271 .to_log(T::ContractAddress::get()),272 );273274 self.save()275 }276277 278 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {279 self.check_is_internal()?;280 self.check_is_owner_or_admin(sender)?;281282 self.collection.sponsorship = SponsorshipState::Disabled;283284 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));285 <PalletEvm<T>>::deposit_log(286 erc::CollectionHelpersEvents::CollectionChanged {287 collection_id: eth::collection_id_to_address(self.id),288 }289 .to_log(T::ContractAddress::get()),290 );291 self.save()292 }293294 295 296 297 298 pub fn force_remove_sponsor(&mut self) -> DispatchResult {299 self.check_is_internal()?;300301 self.collection.sponsorship = SponsorshipState::Disabled;302303 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));304 <PalletEvm<T>>::deposit_log(305 erc::CollectionHelpersEvents::CollectionChanged {306 collection_id: eth::collection_id_to_address(self.id),307 }308 .to_log(T::ContractAddress::get()),309 );310 self.save()311 }312313 314 315 pub fn check_is_internal(&self) -> DispatchResult {316 if self.flags.external {317 return Err(<Error<T>>::CollectionIsExternal)?;318 }319320 Ok(())321 }322323 324 325 pub fn check_is_external(&self) -> DispatchResult {326 if !self.flags.external {327 return Err(<Error<T>>::CollectionIsInternal)?;328 }329330 Ok(())331 }332}333334impl<T: Config> Deref for CollectionHandle<T> {335 type Target = Collection<T::AccountId>;336337 fn deref(&self) -> &Self::Target {338 &self.collection339 }340}341342impl<T: Config> DerefMut for CollectionHandle<T> {343 fn deref_mut(&mut self) -> &mut Self::Target {344 &mut self.collection345 }346}347348impl<T: Config> CollectionHandle<T> {349 350 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {351 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);352 Ok(())353 }354355 356 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {357 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))358 }359360 361 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {362 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);363 Ok(())364 }365366 367 368 369 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {370 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)371 }372373 374 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {375 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)376 }377378 379 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {380 ensure!(381 <Allowlist<T>>::get((self.id, user)),382 <Error<T>>::AddressNotInAllowlist383 );384 Ok(())385 }386387 388 389 390 pub fn change_owner(391 &mut self,392 caller: T::CrossAccountId,393 new_owner: T::CrossAccountId,394 ) -> DispatchResult {395 self.check_is_internal()?;396 self.check_is_owner(&caller)?;397 self.collection.owner = new_owner.as_sub().clone();398399 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(400 self.id,401 new_owner.as_sub().clone(),402 ));403 <PalletEvm<T>>::deposit_log(404 erc::CollectionHelpersEvents::CollectionChanged {405 collection_id: eth::collection_id_to_address(self.id),406 }407 .to_log(T::ContractAddress::get()),408 );409410 self.save()411 }412}413414#[frame_support::pallet]415pub mod pallet {416 use super::*;417 use dispatch::CollectionDispatch;418 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};419 use frame_system::pallet_prelude::*;420 use frame_support::traits::Currency;421 use up_data_structs::{TokenId, mapping::TokenAddressMapping};422 use scale_info::TypeInfo;423 use weights::WeightInfo;424425 #[pallet::config]426 pub trait Config:427 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo428 {429 430 type WeightInfo: WeightInfo;431432 433 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;434435 436 type Currency: Currency<Self::AccountId>;437438 439 #[pallet::constant]440 type CollectionCreationPrice: Get<441 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,442 >;443444 445 type CollectionDispatch: CollectionDispatch<Self>;446447 448 type TreasuryAccountId: Get<Self::AccountId>;449450 451 #[pallet::constant]452 type ContractAddress: Get<H160>;453454 455 type EvmTokenAddressMapping: TokenAddressMapping<H160>;456457 458 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;459 }460461 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);462463 #[pallet::pallet]464 #[pallet::storage_version(STORAGE_VERSION)]465 #[pallet::generate_store(pub(super) trait Store)]466 pub struct Pallet<T>(_);467468 #[pallet::extra_constants]469 impl<T: Config> Pallet<T> {470 471 pub fn collection_admins_limit() -> u32 {472 COLLECTION_ADMINS_LIMIT473 }474 }475476 impl<T: Config> Pallet<T> {477 478 pub fn deposit_event(event: Event<T>) {479 let event = <T as Config>::RuntimeEvent::from(event);480 let event = event.into();481 <frame_system::Pallet<T>>::deposit_event(event)482 }483 }484485 #[pallet::event]486 pub enum Event<T: Config> {487 488 CollectionCreated(489 490 CollectionId,491 492 u8,493 494 T::AccountId,495 ),496497 498 CollectionDestroyed(499 500 CollectionId,501 ),502503 504 ItemCreated(505 506 CollectionId,507 508 TokenId,509 510 T::CrossAccountId,511 512 u128,513 ),514515 516 ItemDestroyed(517 518 CollectionId,519 520 TokenId,521 522 T::CrossAccountId,523 524 u128,525 ),526527 528 Transfer(529 530 CollectionId,531 532 TokenId,533 534 T::CrossAccountId,535 536 T::CrossAccountId,537 538 u128,539 ),540541 542 Approved(543 544 CollectionId,545 546 TokenId,547 548 T::CrossAccountId,549 550 T::CrossAccountId,551 552 u128,553 ),554555 556 ApprovedForAll(557 558 CollectionId,559 560 T::CrossAccountId,561 562 T::CrossAccountId,563 564 bool,565 ),566567 568 CollectionPropertySet(569 570 CollectionId,571 572 PropertyKey,573 ),574575 576 CollectionPropertyDeleted(577 578 CollectionId,579 580 PropertyKey,581 ),582583 584 TokenPropertySet(585 586 CollectionId,587 588 TokenId,589 590 PropertyKey,591 ),592593 594 TokenPropertyDeleted(595 596 CollectionId,597 598 TokenId,599 600 PropertyKey,601 ),602603 604 PropertyPermissionSet(605 606 CollectionId,607 608 PropertyKey,609 ),610611 612 AllowListAddressAdded(613 614 CollectionId,615 616 T::CrossAccountId,617 ),618619 620 AllowListAddressRemoved(621 622 CollectionId,623 624 T::CrossAccountId,625 ),626627 628 CollectionAdminAdded(629 630 CollectionId,631 632 T::CrossAccountId,633 ),634635 636 CollectionAdminRemoved(637 638 CollectionId,639 640 T::CrossAccountId,641 ),642643 644 CollectionLimitSet(645 646 CollectionId,647 ),648649 650 CollectionOwnerChanged(651 652 CollectionId,653 654 T::AccountId,655 ),656657 658 CollectionPermissionSet(659 660 CollectionId,661 ),662663 664 CollectionSponsorSet(665 666 CollectionId,667 668 T::AccountId,669 ),670671 672 SponsorshipConfirmed(673 674 CollectionId,675 676 T::AccountId,677 ),678679 680 CollectionSponsorRemoved(681 682 CollectionId,683 ),684 }685686 #[pallet::error]687 pub enum Error<T> {688 689 CollectionNotFound,690 691 MustBeTokenOwner,692 693 NoPermission,694 695 CantDestroyNotEmptyCollection,696 697 PublicMintingNotAllowed,698 699 AddressNotInAllowlist,700701 702 CollectionNameLimitExceeded,703 704 CollectionDescriptionLimitExceeded,705 706 CollectionTokenPrefixLimitExceeded,707 708 TotalCollectionsLimitExceeded,709 710 CollectionAdminCountExceeded,711 712 CollectionLimitBoundsExceeded,713 714 OwnerPermissionsCantBeReverted,715 716 TransferNotAllowed,717 718 AccountTokenLimitExceeded,719 720 CollectionTokenLimitExceeded,721 722 MetadataFlagFrozen,723724 725 TokenNotFound,726 727 TokenValueTooLow,728 729 ApprovedValueTooLow,730 731 CantApproveMoreThanOwned,732 733 AddressIsNotEthMirror,734735 736 AddressIsZero,737738 739 UnsupportedOperation,740741 742 NotSufficientFounds,743744 745 UserIsNotAllowedToNest,746 747 SourceCollectionIsNotAllowedToNest,748749 750 CollectionFieldSizeExceeded,751752 753 NoSpaceForProperty,754755 756 PropertyLimitReached,757758 759 PropertyKeyIsTooLong,760761 762 InvalidCharacterInPropertyKey,763764 765 EmptyPropertyKey,766767 768 CollectionIsExternal,769770 771 CollectionIsInternal,772773 774 ConfirmSponsorshipFail,775776 777 UserIsNotCollectionAdmin,778 }779780 781 #[pallet::storage]782 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;783784 785 #[pallet::storage]786 pub type DestroyedCollectionCount<T> =787 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;788789 790 #[pallet::storage]791 pub type CollectionById<T> = StorageMap<792 Hasher = Blake2_128Concat,793 Key = CollectionId,794 Value = Collection<<T as frame_system::Config>::AccountId>,795 QueryKind = OptionQuery,796 >;797798 799 #[pallet::storage]800 #[pallet::getter(fn collection_properties)]801 pub type CollectionProperties<T> = StorageMap<802 Hasher = Blake2_128Concat,803 Key = CollectionId,804 Value = CollectionPropertiesT,805 QueryKind = ValueQuery,806 >;807808 809 #[pallet::storage]810 #[pallet::getter(fn property_permissions)]811 pub type CollectionPropertyPermissions<T> = StorageMap<812 Hasher = Blake2_128Concat,813 Key = CollectionId,814 Value = PropertiesPermissionMap,815 QueryKind = ValueQuery,816 >;817818 819 #[pallet::storage]820 pub type AdminAmount<T> = StorageMap<821 Hasher = Blake2_128Concat,822 Key = CollectionId,823 Value = u32,824 QueryKind = ValueQuery,825 >;826827 828 #[pallet::storage]829 pub type IsAdmin<T: Config> = StorageNMap<830 Key = (831 Key<Blake2_128Concat, CollectionId>,832 Key<Blake2_128Concat, T::CrossAccountId>,833 ),834 Value = bool,835 QueryKind = ValueQuery,836 >;837838 839 #[pallet::storage]840 pub type Allowlist<T: Config> = StorageNMap<841 Key = (842 Key<Blake2_128Concat, CollectionId>,843 Key<Blake2_128Concat, T::CrossAccountId>,844 ),845 Value = bool,846 QueryKind = ValueQuery,847 >;848849 850 #[pallet::storage]851 pub type DummyStorageValue<T: Config> = StorageValue<852 Value = (853 CollectionStats,854 CollectionId,855 TokenId,856 TokenChild,857 PhantomType<(858 TokenData<T::CrossAccountId>,859 RpcCollection<T::AccountId>,860 861 PovInfo,862 )>,863 ),864 QueryKind = OptionQuery,865 >;866867 #[pallet::hooks]868 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {869 fn on_runtime_upgrade() -> Weight {870 StorageVersion::new(1).put::<Pallet<T>>();871872 Weight::zero()873 }874 }875}876877impl<T: Config> Pallet<T> {878 879 880 881 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {882 ensure!(883 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,884 <Error<T>>::AddressIsZero885 );886 Ok(())887 }888889 890 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {891 <IsAdmin<T>>::iter_prefix((collection,))892 .map(|(a, _)| a)893 .collect()894 }895896 897 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {898 <Allowlist<T>>::iter_prefix((collection,))899 .map(|(a, _)| a)900 .collect()901 }902903 904 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {905 <Allowlist<T>>::get((collection, user))906 }907908 909 pub fn collection_stats() -> CollectionStats {910 let created = <CreatedCollectionCount<T>>::get();911 let destroyed = <DestroyedCollectionCount<T>>::get();912 CollectionStats {913 created: created.0,914 destroyed: destroyed.0,915 alive: created.0 - destroyed.0,916 }917 }918919 920 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {921 let collection = <CollectionById<T>>::get(collection)?;922 let limits = collection.limits;923 let effective_limits = CollectionLimits {924 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),925 sponsored_data_size: Some(limits.sponsored_data_size()),926 sponsored_data_rate_limit: Some(927 limits928 .sponsored_data_rate_limit929 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),930 ),931 token_limit: Some(limits.token_limit()),932 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(933 match collection.mode {934 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,935 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,936 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,937 },938 )),939 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),940 owner_can_transfer: Some(limits.owner_can_transfer()),941 owner_can_destroy: Some(limits.owner_can_destroy()),942 transfers_enabled: Some(limits.transfers_enabled()),943 };944945 Some(effective_limits)946 }947948 949 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {950 let Collection {951 name,952 description,953 owner,954 mode,955 token_prefix,956 sponsorship,957 limits,958 permissions,959 flags,960 } = <CollectionById<T>>::get(collection)?;961962 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)963 .into_iter()964 .map(|(key, permission)| PropertyKeyPermission { key, permission })965 .collect();966967 let properties = <CollectionProperties<T>>::get(collection)968 .into_iter()969 .map(|(key, value)| Property { key, value })970 .collect();971972 let permissions = CollectionPermissions {973 access: Some(permissions.access()),974 mint_mode: Some(permissions.mint_mode()),975 nesting: Some(permissions.nesting().clone()),976 };977978 Some(RpcCollection {979 name: name.into_inner(),980 description: description.into_inner(),981 owner,982 mode,983 token_prefix: token_prefix.into_inner(),984 sponsorship,985 limits,986 permissions,987 token_property_permissions,988 properties,989 read_only: flags.external,990991 flags: RpcCollectionFlags {992 foreign: flags.foreign,993 erc721metadata: flags.erc721metadata,994 },995 })996 }997}998999macro_rules! limit_default {1000 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1001 $(1002 if let Some($new) = $new.$field {1003 let $old = $old.$field($($arg)?);1004 let _ = $new;1005 let _ = $old;1006 $check1007 } else {1008 $new.$field = $old.$field1009 }1010 )*1011 }};1012}1013macro_rules! limit_default_clone {1014 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1015 $(1016 if let Some($new) = $new.$field.clone() {1017 let $old = $old.$field($($arg)?);1018 let _ = $new;1019 let _ = $old;1020 $check1021 } else {1022 $new.$field = $old.$field.clone()1023 }1024 )*1025 }};1026}10271028impl<T: Config> Pallet<T> {1029 1030 1031 1032 1033 1034 pub fn init_collection(1035 owner: T::CrossAccountId,1036 payer: T::CrossAccountId,1037 data: CreateCollectionData<T::AccountId>,1038 flags: CollectionFlags,1039 ) -> Result<CollectionId, DispatchError> {1040 {1041 ensure!(1042 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1043 Error::<T>::CollectionTokenPrefixLimitExceeded1044 );1045 }10461047 let created_count = <CreatedCollectionCount<T>>::get()1048 .01049 .checked_add(1)1050 .ok_or(ArithmeticError::Overflow)?;1051 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1052 let id = CollectionId(created_count);10531054 1055 ensure!(1056 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1057 <Error<T>>::TotalCollectionsLimitExceeded1058 );10591060 10611062 let collection = Collection {1063 owner: owner.as_sub().clone(),1064 name: data.name,1065 mode: data.mode.clone(),1066 description: data.description,1067 token_prefix: data.token_prefix,1068 sponsorship: data1069 .pending_sponsor1070 .map(SponsorshipState::Unconfirmed)1071 .unwrap_or_default(),1072 limits: data1073 .limits1074 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1075 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1076 permissions: data1077 .permissions1078 .map(|permissions| {1079 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1080 })1081 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1082 flags,1083 };10841085 let mut collection_properties = CollectionPropertiesT::new();1086 collection_properties1087 .try_set_from_iter(data.properties.into_iter())1088 .map_err(<Error<T>>::from)?;10891090 CollectionProperties::<T>::insert(id, collection_properties);10911092 let mut token_props_permissions = PropertiesPermissionMap::new();1093 token_props_permissions1094 .try_set_from_iter(data.token_property_permissions.into_iter())1095 .map_err(<Error<T>>::from)?;10961097 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);10981099 1100 {1101 let mut imbalance =1102 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1103 imbalance.subsume(1104 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1105 &T::TreasuryAccountId::get(),1106 T::CollectionCreationPrice::get(),1107 ),1108 );1109 <T as Config>::Currency::settle(1110 payer.as_sub(),1111 imbalance,1112 WithdrawReasons::TRANSFER,1113 ExistenceRequirement::KeepAlive,1114 )1115 .map_err(|_| Error::<T>::NotSufficientFounds)?;1116 }11171118 <CreatedCollectionCount<T>>::put(created_count);1119 <Pallet<T>>::deposit_event(Event::CollectionCreated(1120 id,1121 data.mode.id(),1122 owner.as_sub().clone(),1123 ));1124 <PalletEvm<T>>::deposit_log(1125 erc::CollectionHelpersEvents::CollectionCreated {1126 owner: *owner.as_eth(),1127 collection_id: eth::collection_id_to_address(id),1128 }1129 .to_log(T::ContractAddress::get()),1130 );1131 <CollectionById<T>>::insert(id, collection);1132 Ok(id)1133 }11341135 1136 1137 1138 1139 pub fn destroy_collection(1140 collection: CollectionHandle<T>,1141 sender: &T::CrossAccountId,1142 ) -> DispatchResult {1143 ensure!(1144 collection.limits.owner_can_destroy(),1145 <Error<T>>::NoPermission,1146 );1147 collection.check_is_owner(sender)?;11481149 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1150 .01151 .checked_add(1)1152 .ok_or(ArithmeticError::Overflow)?;11531154 11551156 <DestroyedCollectionCount<T>>::put(destroyed_collections);1157 <CollectionById<T>>::remove(collection.id);1158 <AdminAmount<T>>::remove(collection.id);1159 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1160 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1161 <CollectionProperties<T>>::remove(collection.id);11621163 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11641165 <PalletEvm<T>>::deposit_log(1166 erc::CollectionHelpersEvents::CollectionDestroyed {1167 collection_id: eth::collection_id_to_address(collection.id),1168 }1169 .to_log(T::ContractAddress::get()),1170 );1171 Ok(())1172 }11731174 1175 1176 1177 1178 1179 1180 1181 1182 #[transactional]1183 fn modify_collection_properties(1184 collection: &CollectionHandle<T>,1185 sender: &T::CrossAccountId,1186 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1187 ) -> DispatchResult {1188 collection.check_is_owner_or_admin(sender)?;11891190 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);11911192 for (key, value) in properties_updates {1193 match value {1194 Some(value) => {1195 stored_properties1196 .try_set(key.clone(), value)1197 .map_err(<Error<T>>::from)?;11981199 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1200 <PalletEvm<T>>::deposit_log(1201 erc::CollectionHelpersEvents::CollectionChanged {1202 collection_id: eth::collection_id_to_address(collection.id),1203 }1204 .to_log(T::ContractAddress::get()),1205 );1206 }1207 None => {1208 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12091210 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1211 <PalletEvm<T>>::deposit_log(1212 erc::CollectionHelpersEvents::CollectionChanged {1213 collection_id: eth::collection_id_to_address(collection.id),1214 }1215 .to_log(T::ContractAddress::get()),1216 );1217 }1218 }1219 }12201221 <CollectionProperties<T>>::set(collection.id, stored_properties);12221223 Ok(())1224 }12251226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 pub fn modify_token_properties(1244 collection: &CollectionHandle<T>,1245 sender: &T::CrossAccountId,1246 token_id: TokenId,1247 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1248 is_token_create: bool,1249 mut stored_properties: TokenProperties,1250 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1251 set_token_properties: impl FnOnce(TokenProperties),1252 log: evm_coder::ethereum::Log,1253 ) -> DispatchResult {1254 let is_collection_admin = collection.is_owner_or_admin(sender);1255 let permissions = Self::property_permissions(collection.id);12561257 let mut token_owner_result = None;1258 let mut is_token_owner = || -> Result<bool, DispatchError> {1259 *token_owner_result.get_or_insert_with(&is_token_owner)1260 };12611262 for (key, value) in properties_updates {1263 let permission = permissions1264 .get(&key)1265 .cloned()1266 .unwrap_or_else(PropertyPermission::none);12671268 let is_property_exists = stored_properties.get(&key).is_some();12691270 match permission {1271 PropertyPermission { mutable: false, .. } if is_property_exists => {1272 return Err(<Error<T>>::NoPermission.into());1273 }12741275 PropertyPermission {1276 collection_admin,1277 token_owner,1278 ..1279 } => {1280 1281 let is_token_create =1282 is_token_create && (collection_admin || token_owner) && value.is_some();1283 if !(is_token_create1284 || (collection_admin && is_collection_admin)1285 || (token_owner && is_token_owner()?))1286 {1287 fail!(<Error<T>>::NoPermission);1288 }1289 }1290 }12911292 match value {1293 Some(value) => {1294 stored_properties1295 .try_set(key.clone(), value)1296 .map_err(<Error<T>>::from)?;12971298 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1299 }1300 None => {1301 stored_properties.remove(&key).map_err(<Error<T>>::from)?;13021303 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1304 }1305 }13061307 <PalletEvm<T>>::deposit_log(log.clone());1308 }13091310 set_token_properties(stored_properties);13111312 Ok(())1313 }13141315 1316 1317 1318 1319 1320 1321 pub fn set_allowance_for_all(1322 collection: &CollectionHandle<T>,1323 owner: &T::CrossAccountId,1324 operator: &T::CrossAccountId,1325 approve: bool,1326 set_allowance: impl FnOnce(),1327 log: evm_coder::ethereum::Log,1328 ) -> DispatchResult {1329 if collection.permissions.access() == AccessMode::AllowList {1330 collection.check_allowlist(owner)?;1331 collection.check_allowlist(operator)?;1332 }13331334 Self::ensure_correct_receiver(operator)?;13351336 set_allowance();13371338 <PalletEvm<T>>::deposit_log(log);1339 Self::deposit_event(Event::ApprovedForAll(1340 collection.id,1341 owner.clone(),1342 operator.clone(),1343 approve,1344 ));1345 Ok(())1346 }13471348 1349 1350 1351 1352 1353 pub fn set_collection_property(1354 collection: &CollectionHandle<T>,1355 sender: &T::CrossAccountId,1356 property: Property,1357 ) -> DispatchResult {1358 Self::set_collection_properties(collection, sender, [property].into_iter())1359 }13601361 1362 1363 1364 1365 1366 1367 pub fn set_scoped_collection_property(1368 collection_id: CollectionId,1369 scope: PropertyScope,1370 property: Property,1371 ) -> DispatchResult {1372 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1373 properties.try_scoped_set(scope, property.key, property.value)1374 })1375 .map_err(<Error<T>>::from)?;13761377 Ok(())1378 }13791380 1381 1382 1383 1384 1385 1386 pub fn set_scoped_collection_properties(1387 collection_id: CollectionId,1388 scope: PropertyScope,1389 properties: impl Iterator<Item = Property>,1390 ) -> DispatchResult {1391 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1392 stored_properties.try_scoped_set_from_iter(scope, properties)1393 })1394 .map_err(<Error<T>>::from)?;13951396 Ok(())1397 }13981399 1400 1401 1402 1403 1404 pub fn set_collection_properties(1405 collection: &CollectionHandle<T>,1406 sender: &T::CrossAccountId,1407 properties: impl Iterator<Item = Property>,1408 ) -> DispatchResult {1409 Self::modify_collection_properties(1410 collection,1411 sender,1412 properties.map(|property| (property.key, Some(property.value))),1413 )1414 }14151416 1417 1418 1419 1420 1421 pub fn delete_collection_property(1422 collection: &CollectionHandle<T>,1423 sender: &T::CrossAccountId,1424 property_key: PropertyKey,1425 ) -> DispatchResult {1426 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1427 }14281429 1430 1431 1432 1433 1434 pub fn delete_collection_properties(1435 collection: &CollectionHandle<T>,1436 sender: &T::CrossAccountId,1437 property_keys: impl Iterator<Item = PropertyKey>,1438 ) -> DispatchResult {1439 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1440 }14411442 1443 1444 1445 1446 1447 1448 pub fn set_property_permission_unchecked(1449 collection: CollectionId,1450 property_permission: PropertyKeyPermission,1451 ) -> DispatchResult {1452 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1453 permissions.try_set(property_permission.key, property_permission.permission)1454 })1455 .map_err(<Error<T>>::from)?;1456 Ok(())1457 }14581459 1460 1461 1462 1463 1464 pub fn set_property_permission(1465 collection: &CollectionHandle<T>,1466 sender: &T::CrossAccountId,1467 property_permission: PropertyKeyPermission,1468 ) -> DispatchResult {1469 Self::set_scoped_property_permission(1470 collection,1471 sender,1472 PropertyScope::None,1473 property_permission,1474 )1475 }14761477 1478 1479 1480 1481 1482 1483 pub fn set_scoped_property_permission(1484 collection: &CollectionHandle<T>,1485 sender: &T::CrossAccountId,1486 scope: PropertyScope,1487 property_permission: PropertyKeyPermission,1488 ) -> DispatchResult {1489 collection.check_is_owner_or_admin(sender)?;14901491 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1492 let current_permission = all_permissions.get(&property_permission.key);1493 if matches![1494 current_permission,1495 Some(PropertyPermission { mutable: false, .. })1496 ] {1497 return Err(<Error<T>>::NoPermission.into());1498 }14991500 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1501 let property_permission = property_permission.clone();1502 permissions.try_scoped_set(1503 scope,1504 property_permission.key,1505 property_permission.permission,1506 )1507 })1508 .map_err(<Error<T>>::from)?;15091510 Self::deposit_event(Event::PropertyPermissionSet(1511 collection.id,1512 property_permission.key,1513 ));1514 <PalletEvm<T>>::deposit_log(1515 erc::CollectionHelpersEvents::CollectionChanged {1516 collection_id: eth::collection_id_to_address(collection.id),1517 }1518 .to_log(T::ContractAddress::get()),1519 );15201521 Ok(())1522 }15231524 1525 1526 1527 1528 1529 #[transactional]1530 pub fn set_token_property_permissions(1531 collection: &CollectionHandle<T>,1532 sender: &T::CrossAccountId,1533 property_permissions: Vec<PropertyKeyPermission>,1534 ) -> DispatchResult {1535 Self::set_scoped_token_property_permissions(1536 collection,1537 sender,1538 PropertyScope::None,1539 property_permissions,1540 )1541 }15421543 1544 1545 1546 1547 1548 1549 #[transactional]1550 pub fn set_scoped_token_property_permissions(1551 collection: &CollectionHandle<T>,1552 sender: &T::CrossAccountId,1553 scope: PropertyScope,1554 property_permissions: Vec<PropertyKeyPermission>,1555 ) -> DispatchResult {1556 for prop_pemission in property_permissions {1557 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1558 }15591560 Ok(())1561 }15621563 1564 pub fn get_collection_property(1565 collection_id: CollectionId,1566 key: &PropertyKey,1567 ) -> Option<PropertyValue> {1568 Self::collection_properties(collection_id).get(key).cloned()1569 }15701571 1572 pub fn bytes_keys_to_property_keys(1573 keys: Vec<Vec<u8>>,1574 ) -> Result<Vec<PropertyKey>, DispatchError> {1575 keys.into_iter()1576 .map(|key| -> Result<PropertyKey, DispatchError> {1577 key.try_into()1578 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1579 })1580 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1581 }15821583 1584 pub fn filter_collection_properties(1585 collection_id: CollectionId,1586 keys: Option<Vec<PropertyKey>>,1587 ) -> Result<Vec<Property>, DispatchError> {1588 let properties = Self::collection_properties(collection_id);15891590 let properties = keys1591 .map(|keys| {1592 keys.into_iter()1593 .filter_map(|key| {1594 properties.get(&key).map(|value| Property {1595 key,1596 value: value.clone(),1597 })1598 })1599 .collect()1600 })1601 .unwrap_or_else(|| {1602 properties1603 .into_iter()1604 .map(|(key, value)| Property { key, value })1605 .collect()1606 });16071608 Ok(properties)1609 }16101611 1612 pub fn filter_property_permissions(1613 collection_id: CollectionId,1614 keys: Option<Vec<PropertyKey>>,1615 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1616 let permissions = Self::property_permissions(collection_id);16171618 let key_permissions = keys1619 .map(|keys| {1620 keys.into_iter()1621 .filter_map(|key| {1622 permissions1623 .get(&key)1624 .map(|permission| PropertyKeyPermission {1625 key,1626 permission: permission.clone(),1627 })1628 })1629 .collect()1630 })1631 .unwrap_or_else(|| {1632 permissions1633 .into_iter()1634 .map(|(key, permission)| PropertyKeyPermission { key, permission })1635 .collect()1636 });16371638 Ok(key_permissions)1639 }16401641 1642 1643 1644 pub fn toggle_allowlist(1645 collection: &CollectionHandle<T>,1646 sender: &T::CrossAccountId,1647 user: &T::CrossAccountId,1648 allowed: bool,1649 ) -> DispatchResult {1650 collection.check_is_owner_or_admin(sender)?;16511652 16531654 if allowed {1655 <Allowlist<T>>::insert((collection.id, user), true);1656 Self::deposit_event(Event::<T>::AllowListAddressAdded(1657 collection.id,1658 user.clone(),1659 ));1660 } else {1661 <Allowlist<T>>::remove((collection.id, user));1662 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1663 collection.id,1664 user.clone(),1665 ));1666 }16671668 <PalletEvm<T>>::deposit_log(1669 erc::CollectionHelpersEvents::CollectionChanged {1670 collection_id: eth::collection_id_to_address(collection.id),1671 }1672 .to_log(T::ContractAddress::get()),1673 );16741675 Ok(())1676 }16771678 1679 1680 1681 pub fn toggle_admin(1682 collection: &CollectionHandle<T>,1683 sender: &T::CrossAccountId,1684 user: &T::CrossAccountId,1685 admin: bool,1686 ) -> DispatchResult {1687 collection.check_is_internal()?;1688 collection.check_is_owner(sender)?;16891690 let is_admin = <IsAdmin<T>>::get((collection.id, user));1691 if is_admin == admin {1692 if admin {1693 return Ok(());1694 } else {1695 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1696 }1697 }1698 let amount = <AdminAmount<T>>::get(collection.id);16991700 17011702 if admin {1703 let amount = amount1704 .checked_add(1)1705 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1706 ensure!(1707 amount <= Self::collection_admins_limit(),1708 <Error<T>>::CollectionAdminCountExceeded,1709 );17101711 <AdminAmount<T>>::insert(collection.id, amount);1712 <IsAdmin<T>>::insert((collection.id, user), true);17131714 Self::deposit_event(Event::<T>::CollectionAdminAdded(1715 collection.id,1716 user.clone(),1717 ));1718 } else {1719 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1720 <IsAdmin<T>>::remove((collection.id, user));17211722 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1723 collection.id,1724 user.clone(),1725 ));1726 }17271728 <PalletEvm<T>>::deposit_log(1729 erc::CollectionHelpersEvents::CollectionChanged {1730 collection_id: eth::collection_id_to_address(collection.id),1731 }1732 .to_log(T::ContractAddress::get()),1733 );17341735 Ok(())1736 }17371738 1739 pub fn update_limits(1740 user: &T::CrossAccountId,1741 collection: &mut CollectionHandle<T>,1742 new_limit: CollectionLimits,1743 ) -> DispatchResult {1744 collection.check_is_internal()?;1745 collection.check_is_owner_or_admin(user)?;17461747 collection.limits =1748 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;17491750 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1751 <PalletEvm<T>>::deposit_log(1752 erc::CollectionHelpersEvents::CollectionChanged {1753 collection_id: eth::collection_id_to_address(collection.id),1754 }1755 .to_log(T::ContractAddress::get()),1756 );17571758 collection.save()1759 }17601761 1762 fn clamp_limits(1763 mode: CollectionMode,1764 old_limit: &CollectionLimits,1765 mut new_limit: CollectionLimits,1766 ) -> Result<CollectionLimits, DispatchError> {1767 let limits = old_limit;1768 limit_default!(old_limit, new_limit,1769 account_token_ownership_limit => ensure!(1770 new_limit <= MAX_TOKEN_OWNERSHIP,1771 <Error<T>>::CollectionLimitBoundsExceeded,1772 ),1773 sponsored_data_size => ensure!(1774 new_limit <= CUSTOM_DATA_LIMIT,1775 <Error<T>>::CollectionLimitBoundsExceeded,1776 ),17771778 sponsored_data_rate_limit => {},1779 token_limit => ensure!(1780 old_limit >= new_limit && new_limit > 0,1781 <Error<T>>::CollectionTokenLimitExceeded1782 ),17831784 sponsor_transfer_timeout(match mode {1785 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1786 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1787 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1788 }) => ensure!(1789 new_limit <= MAX_SPONSOR_TIMEOUT,1790 <Error<T>>::CollectionLimitBoundsExceeded,1791 ),1792 sponsor_approve_timeout => {},1793 owner_can_transfer => ensure!(1794 !limits.owner_can_transfer_instaled() ||1795 old_limit || !new_limit,1796 <Error<T>>::OwnerPermissionsCantBeReverted,1797 ),1798 owner_can_destroy => ensure!(1799 old_limit || !new_limit,1800 <Error<T>>::OwnerPermissionsCantBeReverted,1801 ),1802 transfers_enabled => {},1803 );1804 Ok(new_limit)1805 }18061807 1808 pub fn update_permissions(1809 user: &T::CrossAccountId,1810 collection: &mut CollectionHandle<T>,1811 new_permission: CollectionPermissions,1812 ) -> DispatchResult {1813 collection.check_is_internal()?;1814 collection.check_is_owner_or_admin(user)?;1815 collection.permissions = Self::clamp_permissions(1816 collection.mode.clone(),1817 &collection.permissions,1818 new_permission,1819 )?;18201821 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1822 <PalletEvm<T>>::deposit_log(1823 erc::CollectionHelpersEvents::CollectionChanged {1824 collection_id: eth::collection_id_to_address(collection.id),1825 }1826 .to_log(T::ContractAddress::get()),1827 );18281829 collection.save()1830 }18311832 1833 fn clamp_permissions(1834 _mode: CollectionMode,1835 old_permission: &CollectionPermissions,1836 mut new_permission: CollectionPermissions,1837 ) -> Result<CollectionPermissions, DispatchError> {1838 limit_default_clone!(old_permission, new_permission,1839 access => {},1840 mint_mode => {},1841 nesting => { },1842 );1843 Ok(new_permission)1844 }18451846 1847 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1848 CollectionProperties::<T>::mutate(collection_id, |properties| {1849 properties.recompute_consumed_space();1850 });18511852 Ok(())1853 }1854}185518561857#[macro_export]1858macro_rules! unsupported {1859 ($runtime:path) => {1860 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1861 };1862}186318641865pub trait CommonWeightInfo<CrossAccountId> {1866 1867 fn create_item(data: &CreateItemData) -> Weight {1868 Self::create_multiple_items(from_ref(data))1869 }18701871 1872 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;18731874 1875 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;18761877 1878 fn burn_item() -> Weight;18791880 1881 1882 1883 fn set_collection_properties(amount: u32) -> Weight;18841885 1886 1887 1888 fn delete_collection_properties(amount: u32) -> Weight;18891890 1891 1892 1893 fn set_token_properties(amount: u32) -> Weight;18941895 1896 1897 1898 fn delete_token_properties(amount: u32) -> Weight;18991900 1901 1902 1903 fn set_token_property_permissions(amount: u32) -> Weight;19041905 1906 fn transfer() -> Weight;19071908 1909 fn approve() -> Weight;19101911 1912 fn approve_from() -> Weight;19131914 1915 fn transfer_from() -> Weight;19161917 1918 fn burn_from() -> Weight;19191920 1921 1922 1923 1924 fn burn_recursively_self_raw() -> Weight;19251926 1927 1928 1929 fn burn_recursively_breadth_raw(amount: u32) -> Weight;19301931 1932 1933 1934 1935 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1936 Self::burn_recursively_self_raw()1937 .saturating_mul(max_selfs.max(1) as u64)1938 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1939 }19401941 1942 fn token_owner() -> Weight;19431944 1945 fn set_allowance_for_all() -> Weight;19461947 1948 fn force_repair_item() -> Weight;1949}195019511952pub trait RefungibleExtensionsWeightInfo {1953 1954 fn repartition() -> Weight;1955}195619571958195919601961pub trait CommonCollectionOperations<T: Config> {1962 1963 1964 1965 1966 1967 1968 fn create_item(1969 &self,1970 sender: T::CrossAccountId,1971 to: T::CrossAccountId,1972 data: CreateItemData,1973 nesting_budget: &dyn Budget,1974 ) -> DispatchResultWithPostInfo;19751976 1977 1978 1979 1980 1981 1982 fn create_multiple_items(1983 &self,1984 sender: T::CrossAccountId,1985 to: T::CrossAccountId,1986 data: Vec<CreateItemData>,1987 nesting_budget: &dyn Budget,1988 ) -> DispatchResultWithPostInfo;19891990 1991 1992 1993 1994 1995 1996 fn create_multiple_items_ex(1997 &self,1998 sender: T::CrossAccountId,1999 data: CreateItemExData<T::CrossAccountId>,2000 nesting_budget: &dyn Budget,2001 ) -> DispatchResultWithPostInfo;20022003 2004 2005 2006 2007 2008 fn burn_item(2009 &self,2010 sender: T::CrossAccountId,2011 token: TokenId,2012 amount: u128,2013 ) -> DispatchResultWithPostInfo;20142015 2016 2017 2018 2019 2020 2021 fn burn_item_recursively(2022 &self,2023 sender: T::CrossAccountId,2024 token: TokenId,2025 self_budget: &dyn Budget,2026 breadth_budget: &dyn Budget,2027 ) -> DispatchResultWithPostInfo;20282029 2030 2031 2032 2033 fn set_collection_properties(2034 &self,2035 sender: T::CrossAccountId,2036 properties: Vec<Property>,2037 ) -> DispatchResultWithPostInfo;20382039 2040 2041 2042 2043 fn delete_collection_properties(2044 &self,2045 sender: &T::CrossAccountId,2046 property_keys: Vec<PropertyKey>,2047 ) -> DispatchResultWithPostInfo;20482049 2050 2051 2052 2053 2054 2055 2056 2057 2058 fn set_token_properties(2059 &self,2060 sender: T::CrossAccountId,2061 token_id: TokenId,2062 properties: Vec<Property>,2063 budget: &dyn Budget,2064 ) -> DispatchResultWithPostInfo;20652066 2067 2068 2069 2070 2071 2072 2073 2074 2075 fn delete_token_properties(2076 &self,2077 sender: T::CrossAccountId,2078 token_id: TokenId,2079 property_keys: Vec<PropertyKey>,2080 budget: &dyn Budget,2081 ) -> DispatchResultWithPostInfo;20822083 2084 2085 2086 2087 2088 2089 fn set_token_property_permissions(2090 &self,2091 sender: &T::CrossAccountId,2092 property_permissions: Vec<PropertyKeyPermission>,2093 ) -> DispatchResultWithPostInfo;20942095 2096 2097 2098 2099 2100 2101 2102 fn transfer(2103 &self,2104 sender: T::CrossAccountId,2105 to: T::CrossAccountId,2106 token: TokenId,2107 amount: u128,2108 budget: &dyn Budget,2109 ) -> DispatchResultWithPostInfo;21102111 2112 2113 2114 2115 2116 2117 fn approve(2118 &self,2119 sender: T::CrossAccountId,2120 spender: T::CrossAccountId,2121 token: TokenId,2122 amount: u128,2123 ) -> DispatchResultWithPostInfo;21242125 2126 2127 2128 2129 2130 2131 2132 fn approve_from(2133 &self,2134 sender: T::CrossAccountId,2135 from: T::CrossAccountId,2136 to: T::CrossAccountId,2137 token: TokenId,2138 amount: u128,2139 ) -> DispatchResultWithPostInfo;21402141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 fn transfer_from(2152 &self,2153 sender: T::CrossAccountId,2154 from: T::CrossAccountId,2155 to: T::CrossAccountId,2156 token: TokenId,2157 amount: u128,2158 budget: &dyn Budget,2159 ) -> DispatchResultWithPostInfo;21602161 2162 2163 2164 2165 2166 2167 2168 2169 2170 fn burn_from(2171 &self,2172 sender: T::CrossAccountId,2173 from: T::CrossAccountId,2174 token: TokenId,2175 amount: u128,2176 budget: &dyn Budget,2177 ) -> DispatchResultWithPostInfo;21782179 2180 2181 2182 2183 2184 2185 fn check_nesting(2186 &self,2187 sender: T::CrossAccountId,2188 from: (CollectionId, TokenId),2189 under: TokenId,2190 budget: &dyn Budget,2191 ) -> DispatchResult;21922193 2194 2195 2196 2197 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21982199 2200 2201 2202 2203 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22042205 2206 2207 2208 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;22092210 2211 fn collection_tokens(&self) -> Vec<TokenId>;22122213 2214 2215 2216 fn token_exists(&self, token: TokenId) -> bool;22172218 2219 fn last_token_id(&self) -> TokenId;22202221 2222 2223 2224 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22252226 2227 2228 2229 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22302231 2232 2233 2234 2235 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22362237 2238 2239 2240 2241 2242 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22432244 2245 fn total_supply(&self) -> u32;22462247 2248 2249 2250 fn account_balance(&self, account: T::CrossAccountId) -> u32;22512252 2253 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;22542255 2256 fn total_pieces(&self, token: TokenId) -> Option<u128>;22572258 2259 2260 2261 2262 2263 fn allowance(2264 &self,2265 sender: T::CrossAccountId,2266 spender: T::CrossAccountId,2267 token: TokenId,2268 ) -> u128;22692270 2271 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;22722273 2274 2275 2276 2277 fn set_allowance_for_all(2278 &self,2279 owner: T::CrossAccountId,2280 operator: T::CrossAccountId,2281 approve: bool,2282 ) -> DispatchResultWithPostInfo;22832284 2285 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;22862287 2288 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2289}229022912292pub trait RefungibleExtensions<T>2293where2294 T: Config,2295{2296 2297 2298 2299 2300 2301 2302 2303 fn repartition(2304 &self,2305 sender: &T::CrossAccountId,2306 token: TokenId,2307 amount: u128,2308 ) -> DispatchResultWithPostInfo;2309}23102311231223132314pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2315 let post_info = PostDispatchInfo {2316 actual_weight: Some(weight),2317 pays_fee: Pays::Yes,2318 };2319 match res {2320 Ok(()) => Ok(post_info),2321 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2322 }2323}23242325impl<T: Config> From<PropertiesError> for Error<T> {2326 fn from(error: PropertiesError) -> Self {2327 match error {2328 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2329 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2330 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2331 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2332 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2333 }2334 }2335}