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, Properties, PropertiesPermissionMap, PropertyKey, PropertyValue,80 PropertyPermission, PropertiesError, TokenOwnerError, PropertyKeyPermission, TokenData,81 TrySetProperty, PropertyScope, CollectionPermissions,82};83use up_pov_estimate_rpc::PovInfo;8485pub use pallet::*;86use sp_core::H160;87use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};8889use crate::erc::CollectionHelpersEvents;90#[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 = Properties,805 QueryKind = ValueQuery,806 OnEmpty = up_data_structs::CollectionProperties,807 >;808809 810 #[pallet::storage]811 #[pallet::getter(fn property_permissions)]812 pub type CollectionPropertyPermissions<T> = StorageMap<813 Hasher = Blake2_128Concat,814 Key = CollectionId,815 Value = PropertiesPermissionMap,816 QueryKind = ValueQuery,817 >;818819 820 #[pallet::storage]821 pub type AdminAmount<T> = StorageMap<822 Hasher = Blake2_128Concat,823 Key = CollectionId,824 Value = u32,825 QueryKind = ValueQuery,826 >;827828 829 #[pallet::storage]830 pub type IsAdmin<T: Config> = StorageNMap<831 Key = (832 Key<Blake2_128Concat, CollectionId>,833 Key<Blake2_128Concat, T::CrossAccountId>,834 ),835 Value = bool,836 QueryKind = ValueQuery,837 >;838839 840 #[pallet::storage]841 pub type Allowlist<T: Config> = StorageNMap<842 Key = (843 Key<Blake2_128Concat, CollectionId>,844 Key<Blake2_128Concat, T::CrossAccountId>,845 ),846 Value = bool,847 QueryKind = ValueQuery,848 >;849850 851 #[pallet::storage]852 pub type DummyStorageValue<T: Config> = StorageValue<853 Value = (854 CollectionStats,855 CollectionId,856 TokenId,857 TokenChild,858 PhantomType<(859 TokenData<T::CrossAccountId>,860 RpcCollection<T::AccountId>,861 862 PovInfo,863 )>,864 ),865 QueryKind = OptionQuery,866 >;867868 #[pallet::hooks]869 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {870 fn on_runtime_upgrade() -> Weight {871 StorageVersion::new(1).put::<Pallet<T>>();872873 Weight::zero()874 }875 }876}877878impl<T: Config> Pallet<T> {879 880 881 882 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {883 ensure!(884 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,885 <Error<T>>::AddressIsZero886 );887 Ok(())888 }889890 891 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {892 <IsAdmin<T>>::iter_prefix((collection,))893 .map(|(a, _)| a)894 .collect()895 }896897 898 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {899 <Allowlist<T>>::iter_prefix((collection,))900 .map(|(a, _)| a)901 .collect()902 }903904 905 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {906 <Allowlist<T>>::get((collection, user))907 }908909 910 pub fn collection_stats() -> CollectionStats {911 let created = <CreatedCollectionCount<T>>::get();912 let destroyed = <DestroyedCollectionCount<T>>::get();913 CollectionStats {914 created: created.0,915 destroyed: destroyed.0,916 alive: created.0 - destroyed.0,917 }918 }919920 921 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {922 let collection = <CollectionById<T>>::get(collection)?;923 let limits = collection.limits;924 let effective_limits = CollectionLimits {925 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),926 sponsored_data_size: Some(limits.sponsored_data_size()),927 sponsored_data_rate_limit: Some(928 limits929 .sponsored_data_rate_limit930 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),931 ),932 token_limit: Some(limits.token_limit()),933 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(934 match collection.mode {935 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,936 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,937 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,938 },939 )),940 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),941 owner_can_transfer: Some(limits.owner_can_transfer()),942 owner_can_destroy: Some(limits.owner_can_destroy()),943 transfers_enabled: Some(limits.transfers_enabled()),944 };945946 Some(effective_limits)947 }948949 950 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {951 let Collection {952 name,953 description,954 owner,955 mode,956 token_prefix,957 sponsorship,958 limits,959 permissions,960 flags,961 } = <CollectionById<T>>::get(collection)?;962963 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)964 .into_iter()965 .map(|(key, permission)| PropertyKeyPermission { key, permission })966 .collect();967968 let properties = <CollectionProperties<T>>::get(collection)969 .into_iter()970 .map(|(key, value)| Property { key, value })971 .collect();972973 let permissions = CollectionPermissions {974 access: Some(permissions.access()),975 mint_mode: Some(permissions.mint_mode()),976 nesting: Some(permissions.nesting().clone()),977 };978979 Some(RpcCollection {980 name: name.into_inner(),981 description: description.into_inner(),982 owner,983 mode,984 token_prefix: token_prefix.into_inner(),985 sponsorship,986 limits,987 permissions,988 token_property_permissions,989 properties,990 read_only: flags.external,991992 flags: RpcCollectionFlags {993 foreign: flags.foreign,994 erc721metadata: flags.erc721metadata,995 },996 })997 }998}9991000macro_rules! limit_default {1001 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1002 $(1003 if let Some($new) = $new.$field {1004 let $old = $old.$field($($arg)?);1005 let _ = $new;1006 let _ = $old;1007 $check1008 } else {1009 $new.$field = $old.$field1010 }1011 )*1012 }};1013}1014macro_rules! limit_default_clone {1015 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1016 $(1017 if let Some($new) = $new.$field.clone() {1018 let $old = $old.$field($($arg)?);1019 let _ = $new;1020 let _ = $old;1021 $check1022 } else {1023 $new.$field = $old.$field.clone()1024 }1025 )*1026 }};1027}10281029impl<T: Config> Pallet<T> {1030 1031 1032 1033 1034 1035 pub fn init_collection(1036 owner: T::CrossAccountId,1037 payer: T::CrossAccountId,1038 data: CreateCollectionData<T::AccountId>,1039 flags: CollectionFlags,1040 ) -> Result<CollectionId, DispatchError> {1041 {1042 ensure!(1043 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1044 Error::<T>::CollectionTokenPrefixLimitExceeded1045 );1046 }10471048 let created_count = <CreatedCollectionCount<T>>::get()1049 .01050 .checked_add(1)1051 .ok_or(ArithmeticError::Overflow)?;1052 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1053 let id = CollectionId(created_count);10541055 1056 ensure!(1057 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1058 <Error<T>>::TotalCollectionsLimitExceeded1059 );10601061 10621063 let collection = Collection {1064 owner: owner.as_sub().clone(),1065 name: data.name,1066 mode: data.mode.clone(),1067 description: data.description,1068 token_prefix: data.token_prefix,1069 sponsorship: data1070 .pending_sponsor1071 .map(SponsorshipState::Unconfirmed)1072 .unwrap_or_default(),1073 limits: data1074 .limits1075 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1076 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1077 permissions: data1078 .permissions1079 .map(|permissions| {1080 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1081 })1082 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1083 flags,1084 };10851086 let mut collection_properties = up_data_structs::CollectionProperties::get();1087 collection_properties1088 .try_set_from_iter(data.properties.into_iter())1089 .map_err(<Error<T>>::from)?;10901091 CollectionProperties::<T>::insert(id, collection_properties);10921093 let mut token_props_permissions = PropertiesPermissionMap::new();1094 token_props_permissions1095 .try_set_from_iter(data.token_property_permissions.into_iter())1096 .map_err(<Error<T>>::from)?;10971098 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);10991100 1101 {1102 let mut imbalance =1103 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1104 imbalance.subsume(1105 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1106 &T::TreasuryAccountId::get(),1107 T::CollectionCreationPrice::get(),1108 ),1109 );1110 <T as Config>::Currency::settle(1111 payer.as_sub(),1112 imbalance,1113 WithdrawReasons::TRANSFER,1114 ExistenceRequirement::KeepAlive,1115 )1116 .map_err(|_| Error::<T>::NotSufficientFounds)?;1117 }11181119 <CreatedCollectionCount<T>>::put(created_count);1120 <Pallet<T>>::deposit_event(Event::CollectionCreated(1121 id,1122 data.mode.id(),1123 owner.as_sub().clone(),1124 ));1125 <PalletEvm<T>>::deposit_log(1126 erc::CollectionHelpersEvents::CollectionCreated {1127 owner: *owner.as_eth(),1128 collection_id: eth::collection_id_to_address(id),1129 }1130 .to_log(T::ContractAddress::get()),1131 );1132 <CollectionById<T>>::insert(id, collection);1133 Ok(id)1134 }11351136 1137 1138 1139 1140 pub fn destroy_collection(1141 collection: CollectionHandle<T>,1142 sender: &T::CrossAccountId,1143 ) -> DispatchResult {1144 ensure!(1145 collection.limits.owner_can_destroy(),1146 <Error<T>>::NoPermission,1147 );1148 collection.check_is_owner(sender)?;11491150 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1151 .01152 .checked_add(1)1153 .ok_or(ArithmeticError::Overflow)?;11541155 11561157 <DestroyedCollectionCount<T>>::put(destroyed_collections);1158 <CollectionById<T>>::remove(collection.id);1159 <AdminAmount<T>>::remove(collection.id);1160 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1161 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1162 <CollectionProperties<T>>::remove(collection.id);11631164 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11651166 <PalletEvm<T>>::deposit_log(1167 erc::CollectionHelpersEvents::CollectionDestroyed {1168 collection_id: eth::collection_id_to_address(collection.id),1169 }1170 .to_log(T::ContractAddress::get()),1171 );1172 Ok(())1173 }11741175 1176 1177 1178 1179 1180 1181 1182 1183 #[transactional]1184 fn modify_collection_properties(1185 collection: &CollectionHandle<T>,1186 sender: &T::CrossAccountId,1187 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1188 ) -> DispatchResult {1189 collection.check_is_owner_or_admin(sender)?;11901191 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);11921193 for (key, value) in properties_updates {1194 match value {1195 Some(value) => {1196 stored_properties1197 .try_set(key.clone(), value)1198 .map_err(<Error<T>>::from)?;11991200 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1201 <PalletEvm<T>>::deposit_log(1202 erc::CollectionHelpersEvents::CollectionChanged {1203 collection_id: eth::collection_id_to_address(collection.id),1204 }1205 .to_log(T::ContractAddress::get()),1206 );1207 }1208 None => {1209 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12101211 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1212 <PalletEvm<T>>::deposit_log(1213 erc::CollectionHelpersEvents::CollectionChanged {1214 collection_id: eth::collection_id_to_address(collection.id),1215 }1216 .to_log(T::ContractAddress::get()),1217 );1218 }1219 }1220 }12211222 <CollectionProperties<T>>::set(collection.id, stored_properties);12231224 Ok(())1225 }12261227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 pub fn modify_token_properties(1245 collection: &CollectionHandle<T>,1246 sender: &T::CrossAccountId,1247 token_id: TokenId,1248 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1249 is_token_create: bool,1250 mut stored_properties: Properties,1251 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1252 set_token_properties: impl FnOnce(Properties),1253 log: evm_coder::ethereum::Log,1254 ) -> DispatchResult {1255 let is_collection_admin = collection.is_owner_or_admin(sender);1256 let permissions = Self::property_permissions(collection.id);12571258 let mut token_owner_result = None;1259 let mut is_token_owner = || -> Result<bool, DispatchError> {1260 *token_owner_result.get_or_insert_with(&is_token_owner)1261 };12621263 for (key, value) in properties_updates {1264 let permission = permissions1265 .get(&key)1266 .cloned()1267 .unwrap_or_else(PropertyPermission::none);12681269 let is_property_exists = stored_properties.get(&key).is_some();12701271 match permission {1272 PropertyPermission { mutable: false, .. } if is_property_exists => {1273 return Err(<Error<T>>::NoPermission.into());1274 }12751276 PropertyPermission {1277 collection_admin,1278 token_owner,1279 ..1280 } => {1281 1282 let is_token_create =1283 is_token_create && (collection_admin || token_owner) && value.is_some();1284 if !(is_token_create1285 || (collection_admin && is_collection_admin)1286 || (token_owner && is_token_owner()?))1287 {1288 fail!(<Error<T>>::NoPermission);1289 }1290 }1291 }12921293 match value {1294 Some(value) => {1295 stored_properties1296 .try_set(key.clone(), value)1297 .map_err(<Error<T>>::from)?;12981299 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1300 }1301 None => {1302 stored_properties.remove(&key).map_err(<Error<T>>::from)?;13031304 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1305 }1306 }13071308 <PalletEvm<T>>::deposit_log(log.clone());1309 }13101311 set_token_properties(stored_properties);13121313 Ok(())1314 }13151316 1317 1318 1319 1320 1321 1322 pub fn set_allowance_for_all(1323 collection: &CollectionHandle<T>,1324 owner: &T::CrossAccountId,1325 operator: &T::CrossAccountId,1326 approve: bool,1327 set_allowance: impl FnOnce(),1328 log: evm_coder::ethereum::Log,1329 ) -> DispatchResult {1330 if collection.permissions.access() == AccessMode::AllowList {1331 collection.check_allowlist(owner)?;1332 collection.check_allowlist(operator)?;1333 }13341335 Self::ensure_correct_receiver(operator)?;13361337 set_allowance();13381339 <PalletEvm<T>>::deposit_log(log);1340 Self::deposit_event(Event::ApprovedForAll(1341 collection.id,1342 owner.clone(),1343 operator.clone(),1344 approve,1345 ));1346 Ok(())1347 }13481349 1350 1351 1352 1353 1354 pub fn set_collection_property(1355 collection: &CollectionHandle<T>,1356 sender: &T::CrossAccountId,1357 property: Property,1358 ) -> DispatchResult {1359 Self::set_collection_properties(collection, sender, [property].into_iter())1360 }13611362 1363 1364 1365 1366 1367 1368 pub fn set_scoped_collection_property(1369 collection_id: CollectionId,1370 scope: PropertyScope,1371 property: Property,1372 ) -> DispatchResult {1373 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1374 properties.try_scoped_set(scope, property.key, property.value)1375 })1376 .map_err(<Error<T>>::from)?;13771378 Ok(())1379 }13801381 1382 1383 1384 1385 1386 1387 pub fn set_scoped_collection_properties(1388 collection_id: CollectionId,1389 scope: PropertyScope,1390 properties: impl Iterator<Item = Property>,1391 ) -> DispatchResult {1392 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1393 stored_properties.try_scoped_set_from_iter(scope, properties)1394 })1395 .map_err(<Error<T>>::from)?;13961397 Ok(())1398 }13991400 1401 1402 1403 1404 1405 pub fn set_collection_properties(1406 collection: &CollectionHandle<T>,1407 sender: &T::CrossAccountId,1408 properties: impl Iterator<Item = Property>,1409 ) -> DispatchResult {1410 Self::modify_collection_properties(1411 collection,1412 sender,1413 properties.map(|property| (property.key, Some(property.value))),1414 )1415 }14161417 1418 1419 1420 1421 1422 pub fn delete_collection_property(1423 collection: &CollectionHandle<T>,1424 sender: &T::CrossAccountId,1425 property_key: PropertyKey,1426 ) -> DispatchResult {1427 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1428 }14291430 1431 1432 1433 1434 1435 pub fn delete_collection_properties(1436 collection: &CollectionHandle<T>,1437 sender: &T::CrossAccountId,1438 property_keys: impl Iterator<Item = PropertyKey>,1439 ) -> DispatchResult {1440 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1441 }14421443 1444 1445 1446 1447 1448 1449 pub fn set_property_permission_unchecked(1450 collection: CollectionId,1451 property_permission: PropertyKeyPermission,1452 ) -> DispatchResult {1453 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1454 permissions.try_set(property_permission.key, property_permission.permission)1455 })1456 .map_err(<Error<T>>::from)?;1457 Ok(())1458 }14591460 1461 1462 1463 1464 1465 pub fn set_property_permission(1466 collection: &CollectionHandle<T>,1467 sender: &T::CrossAccountId,1468 property_permission: PropertyKeyPermission,1469 ) -> DispatchResult {1470 Self::set_scoped_property_permission(1471 collection,1472 sender,1473 PropertyScope::None,1474 property_permission,1475 )1476 }14771478 1479 1480 1481 1482 1483 1484 pub fn set_scoped_property_permission(1485 collection: &CollectionHandle<T>,1486 sender: &T::CrossAccountId,1487 scope: PropertyScope,1488 property_permission: PropertyKeyPermission,1489 ) -> DispatchResult {1490 collection.check_is_owner_or_admin(sender)?;14911492 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1493 let current_permission = all_permissions.get(&property_permission.key);1494 if matches![1495 current_permission,1496 Some(PropertyPermission { mutable: false, .. })1497 ] {1498 return Err(<Error<T>>::NoPermission.into());1499 }15001501 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1502 let property_permission = property_permission.clone();1503 permissions.try_scoped_set(1504 scope,1505 property_permission.key,1506 property_permission.permission,1507 )1508 })1509 .map_err(<Error<T>>::from)?;15101511 Self::deposit_event(Event::PropertyPermissionSet(1512 collection.id,1513 property_permission.key,1514 ));1515 <PalletEvm<T>>::deposit_log(1516 erc::CollectionHelpersEvents::CollectionChanged {1517 collection_id: eth::collection_id_to_address(collection.id),1518 }1519 .to_log(T::ContractAddress::get()),1520 );15211522 Ok(())1523 }15241525 1526 1527 1528 1529 1530 #[transactional]1531 pub fn set_token_property_permissions(1532 collection: &CollectionHandle<T>,1533 sender: &T::CrossAccountId,1534 property_permissions: Vec<PropertyKeyPermission>,1535 ) -> DispatchResult {1536 Self::set_scoped_token_property_permissions(1537 collection,1538 sender,1539 PropertyScope::None,1540 property_permissions,1541 )1542 }15431544 1545 1546 1547 1548 1549 1550 #[transactional]1551 pub fn set_scoped_token_property_permissions(1552 collection: &CollectionHandle<T>,1553 sender: &T::CrossAccountId,1554 scope: PropertyScope,1555 property_permissions: Vec<PropertyKeyPermission>,1556 ) -> DispatchResult {1557 for prop_pemission in property_permissions {1558 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1559 }15601561 Ok(())1562 }15631564 1565 pub fn get_collection_property(1566 collection_id: CollectionId,1567 key: &PropertyKey,1568 ) -> Option<PropertyValue> {1569 Self::collection_properties(collection_id).get(key).cloned()1570 }15711572 1573 pub fn bytes_keys_to_property_keys(1574 keys: Vec<Vec<u8>>,1575 ) -> Result<Vec<PropertyKey>, DispatchError> {1576 keys.into_iter()1577 .map(|key| -> Result<PropertyKey, DispatchError> {1578 key.try_into()1579 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1580 })1581 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1582 }15831584 1585 pub fn filter_collection_properties(1586 collection_id: CollectionId,1587 keys: Option<Vec<PropertyKey>>,1588 ) -> Result<Vec<Property>, DispatchError> {1589 let properties = Self::collection_properties(collection_id);15901591 let properties = keys1592 .map(|keys| {1593 keys.into_iter()1594 .filter_map(|key| {1595 properties.get(&key).map(|value| Property {1596 key,1597 value: value.clone(),1598 })1599 })1600 .collect()1601 })1602 .unwrap_or_else(|| {1603 properties1604 .into_iter()1605 .map(|(key, value)| Property { key, value })1606 .collect()1607 });16081609 Ok(properties)1610 }16111612 1613 pub fn filter_property_permissions(1614 collection_id: CollectionId,1615 keys: Option<Vec<PropertyKey>>,1616 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1617 let permissions = Self::property_permissions(collection_id);16181619 let key_permissions = keys1620 .map(|keys| {1621 keys.into_iter()1622 .filter_map(|key| {1623 permissions1624 .get(&key)1625 .map(|permission| PropertyKeyPermission {1626 key,1627 permission: permission.clone(),1628 })1629 })1630 .collect()1631 })1632 .unwrap_or_else(|| {1633 permissions1634 .into_iter()1635 .map(|(key, permission)| PropertyKeyPermission { key, permission })1636 .collect()1637 });16381639 Ok(key_permissions)1640 }16411642 1643 1644 1645 pub fn toggle_allowlist(1646 collection: &CollectionHandle<T>,1647 sender: &T::CrossAccountId,1648 user: &T::CrossAccountId,1649 allowed: bool,1650 ) -> DispatchResult {1651 collection.check_is_owner_or_admin(sender)?;16521653 16541655 if allowed {1656 <Allowlist<T>>::insert((collection.id, user), true);1657 Self::deposit_event(Event::<T>::AllowListAddressAdded(1658 collection.id,1659 user.clone(),1660 ));1661 } else {1662 <Allowlist<T>>::remove((collection.id, user));1663 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1664 collection.id,1665 user.clone(),1666 ));1667 }16681669 <PalletEvm<T>>::deposit_log(1670 erc::CollectionHelpersEvents::CollectionChanged {1671 collection_id: eth::collection_id_to_address(collection.id),1672 }1673 .to_log(T::ContractAddress::get()),1674 );16751676 Ok(())1677 }16781679 1680 1681 1682 pub fn toggle_admin(1683 collection: &CollectionHandle<T>,1684 sender: &T::CrossAccountId,1685 user: &T::CrossAccountId,1686 admin: bool,1687 ) -> DispatchResult {1688 collection.check_is_internal()?;1689 collection.check_is_owner(sender)?;16901691 let is_admin = <IsAdmin<T>>::get((collection.id, user));1692 if is_admin == admin {1693 if admin {1694 return Ok(());1695 } else {1696 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1697 }1698 }1699 let amount = <AdminAmount<T>>::get(collection.id);17001701 17021703 if admin {1704 let amount = amount1705 .checked_add(1)1706 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1707 ensure!(1708 amount <= Self::collection_admins_limit(),1709 <Error<T>>::CollectionAdminCountExceeded,1710 );17111712 <AdminAmount<T>>::insert(collection.id, amount);1713 <IsAdmin<T>>::insert((collection.id, user), true);17141715 Self::deposit_event(Event::<T>::CollectionAdminAdded(1716 collection.id,1717 user.clone(),1718 ));1719 } else {1720 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1721 <IsAdmin<T>>::remove((collection.id, user));17221723 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1724 collection.id,1725 user.clone(),1726 ));1727 }17281729 <PalletEvm<T>>::deposit_log(1730 erc::CollectionHelpersEvents::CollectionChanged {1731 collection_id: eth::collection_id_to_address(collection.id),1732 }1733 .to_log(T::ContractAddress::get()),1734 );17351736 Ok(())1737 }17381739 1740 pub fn update_limits(1741 user: &T::CrossAccountId,1742 collection: &mut CollectionHandle<T>,1743 new_limit: CollectionLimits,1744 ) -> DispatchResult {1745 collection.check_is_internal()?;1746 collection.check_is_owner_or_admin(user)?;17471748 collection.limits =1749 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;17501751 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1752 <PalletEvm<T>>::deposit_log(1753 erc::CollectionHelpersEvents::CollectionChanged {1754 collection_id: eth::collection_id_to_address(collection.id),1755 }1756 .to_log(T::ContractAddress::get()),1757 );17581759 collection.save()1760 }17611762 1763 fn clamp_limits(1764 mode: CollectionMode,1765 old_limit: &CollectionLimits,1766 mut new_limit: CollectionLimits,1767 ) -> Result<CollectionLimits, DispatchError> {1768 let limits = old_limit;1769 limit_default!(old_limit, new_limit,1770 account_token_ownership_limit => ensure!(1771 new_limit <= MAX_TOKEN_OWNERSHIP,1772 <Error<T>>::CollectionLimitBoundsExceeded,1773 ),1774 sponsored_data_size => ensure!(1775 new_limit <= CUSTOM_DATA_LIMIT,1776 <Error<T>>::CollectionLimitBoundsExceeded,1777 ),17781779 sponsored_data_rate_limit => {},1780 token_limit => ensure!(1781 old_limit >= new_limit && new_limit > 0,1782 <Error<T>>::CollectionTokenLimitExceeded1783 ),17841785 sponsor_transfer_timeout(match mode {1786 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1787 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1788 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1789 }) => ensure!(1790 new_limit <= MAX_SPONSOR_TIMEOUT,1791 <Error<T>>::CollectionLimitBoundsExceeded,1792 ),1793 sponsor_approve_timeout => {},1794 owner_can_transfer => ensure!(1795 !limits.owner_can_transfer_instaled() ||1796 old_limit || !new_limit,1797 <Error<T>>::OwnerPermissionsCantBeReverted,1798 ),1799 owner_can_destroy => ensure!(1800 old_limit || !new_limit,1801 <Error<T>>::OwnerPermissionsCantBeReverted,1802 ),1803 transfers_enabled => {},1804 );1805 Ok(new_limit)1806 }18071808 1809 pub fn update_permissions(1810 user: &T::CrossAccountId,1811 collection: &mut CollectionHandle<T>,1812 new_permission: CollectionPermissions,1813 ) -> DispatchResult {1814 collection.check_is_internal()?;1815 collection.check_is_owner_or_admin(user)?;1816 collection.permissions = Self::clamp_permissions(1817 collection.mode.clone(),1818 &collection.permissions,1819 new_permission,1820 )?;18211822 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1823 <PalletEvm<T>>::deposit_log(1824 erc::CollectionHelpersEvents::CollectionChanged {1825 collection_id: eth::collection_id_to_address(collection.id),1826 }1827 .to_log(T::ContractAddress::get()),1828 );18291830 collection.save()1831 }18321833 1834 fn clamp_permissions(1835 _mode: CollectionMode,1836 old_permission: &CollectionPermissions,1837 mut new_permission: CollectionPermissions,1838 ) -> Result<CollectionPermissions, DispatchError> {1839 limit_default_clone!(old_permission, new_permission,1840 access => {},1841 mint_mode => {},1842 nesting => { },1843 );1844 Ok(new_permission)1845 }18461847 1848 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1849 CollectionProperties::<T>::mutate(collection_id, |properties| {1850 properties.recompute_consumed_space();1851 });18521853 Ok(())1854 }1855}185618571858#[macro_export]1859macro_rules! unsupported {1860 ($runtime:path) => {1861 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1862 };1863}186418651866pub trait CommonWeightInfo<CrossAccountId> {1867 1868 fn create_item(data: &CreateItemData) -> Weight {1869 Self::create_multiple_items(from_ref(data))1870 }18711872 1873 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;18741875 1876 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;18771878 1879 fn burn_item() -> Weight;18801881 1882 1883 1884 fn set_collection_properties(amount: u32) -> Weight;18851886 1887 1888 1889 fn delete_collection_properties(amount: u32) -> Weight;18901891 1892 1893 1894 fn set_token_properties(amount: u32) -> Weight;18951896 1897 1898 1899 fn delete_token_properties(amount: u32) -> Weight;19001901 1902 1903 1904 fn set_token_property_permissions(amount: u32) -> Weight;19051906 1907 fn transfer() -> Weight;19081909 1910 fn approve() -> Weight;19111912 1913 fn approve_from() -> Weight;19141915 1916 fn transfer_from() -> Weight;19171918 1919 fn burn_from() -> Weight;19201921 1922 1923 1924 1925 fn burn_recursively_self_raw() -> Weight;19261927 1928 1929 1930 fn burn_recursively_breadth_raw(amount: u32) -> Weight;19311932 1933 1934 1935 1936 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1937 Self::burn_recursively_self_raw()1938 .saturating_mul(max_selfs.max(1) as u64)1939 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1940 }19411942 1943 fn token_owner() -> Weight;19441945 1946 fn set_allowance_for_all() -> Weight;19471948 1949 fn force_repair_item() -> Weight;1950}195119521953pub trait RefungibleExtensionsWeightInfo {1954 1955 fn repartition() -> Weight;1956}195719581959196019611962pub trait CommonCollectionOperations<T: Config> {1963 1964 1965 1966 1967 1968 1969 fn create_item(1970 &self,1971 sender: T::CrossAccountId,1972 to: T::CrossAccountId,1973 data: CreateItemData,1974 nesting_budget: &dyn Budget,1975 ) -> DispatchResultWithPostInfo;19761977 1978 1979 1980 1981 1982 1983 fn create_multiple_items(1984 &self,1985 sender: T::CrossAccountId,1986 to: T::CrossAccountId,1987 data: Vec<CreateItemData>,1988 nesting_budget: &dyn Budget,1989 ) -> DispatchResultWithPostInfo;19901991 1992 1993 1994 1995 1996 1997 fn create_multiple_items_ex(1998 &self,1999 sender: T::CrossAccountId,2000 data: CreateItemExData<T::CrossAccountId>,2001 nesting_budget: &dyn Budget,2002 ) -> DispatchResultWithPostInfo;20032004 2005 2006 2007 2008 2009 fn burn_item(2010 &self,2011 sender: T::CrossAccountId,2012 token: TokenId,2013 amount: u128,2014 ) -> DispatchResultWithPostInfo;20152016 2017 2018 2019 2020 2021 2022 fn burn_item_recursively(2023 &self,2024 sender: T::CrossAccountId,2025 token: TokenId,2026 self_budget: &dyn Budget,2027 breadth_budget: &dyn Budget,2028 ) -> DispatchResultWithPostInfo;20292030 2031 2032 2033 2034 fn set_collection_properties(2035 &self,2036 sender: T::CrossAccountId,2037 properties: Vec<Property>,2038 ) -> DispatchResultWithPostInfo;20392040 2041 2042 2043 2044 fn delete_collection_properties(2045 &self,2046 sender: &T::CrossAccountId,2047 property_keys: Vec<PropertyKey>,2048 ) -> DispatchResultWithPostInfo;20492050 2051 2052 2053 2054 2055 2056 2057 2058 2059 fn set_token_properties(2060 &self,2061 sender: T::CrossAccountId,2062 token_id: TokenId,2063 properties: Vec<Property>,2064 budget: &dyn Budget,2065 ) -> DispatchResultWithPostInfo;20662067 2068 2069 2070 2071 2072 2073 2074 2075 2076 fn delete_token_properties(2077 &self,2078 sender: T::CrossAccountId,2079 token_id: TokenId,2080 property_keys: Vec<PropertyKey>,2081 budget: &dyn Budget,2082 ) -> DispatchResultWithPostInfo;20832084 2085 2086 2087 2088 2089 2090 fn set_token_property_permissions(2091 &self,2092 sender: &T::CrossAccountId,2093 property_permissions: Vec<PropertyKeyPermission>,2094 ) -> DispatchResultWithPostInfo;20952096 2097 2098 2099 2100 2101 2102 2103 fn transfer(2104 &self,2105 sender: T::CrossAccountId,2106 to: T::CrossAccountId,2107 token: TokenId,2108 amount: u128,2109 budget: &dyn Budget,2110 ) -> DispatchResultWithPostInfo;21112112 2113 2114 2115 2116 2117 2118 fn approve(2119 &self,2120 sender: T::CrossAccountId,2121 spender: T::CrossAccountId,2122 token: TokenId,2123 amount: u128,2124 ) -> DispatchResultWithPostInfo;21252126 2127 2128 2129 2130 2131 2132 2133 fn approve_from(2134 &self,2135 sender: T::CrossAccountId,2136 from: T::CrossAccountId,2137 to: T::CrossAccountId,2138 token: TokenId,2139 amount: u128,2140 ) -> DispatchResultWithPostInfo;21412142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 fn transfer_from(2153 &self,2154 sender: T::CrossAccountId,2155 from: T::CrossAccountId,2156 to: T::CrossAccountId,2157 token: TokenId,2158 amount: u128,2159 budget: &dyn Budget,2160 ) -> DispatchResultWithPostInfo;21612162 2163 2164 2165 2166 2167 2168 2169 2170 2171 fn burn_from(2172 &self,2173 sender: T::CrossAccountId,2174 from: T::CrossAccountId,2175 token: TokenId,2176 amount: u128,2177 budget: &dyn Budget,2178 ) -> DispatchResultWithPostInfo;21792180 2181 2182 2183 2184 2185 2186 fn check_nesting(2187 &self,2188 sender: T::CrossAccountId,2189 from: (CollectionId, TokenId),2190 under: TokenId,2191 budget: &dyn Budget,2192 ) -> DispatchResult;21932194 2195 2196 2197 2198 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21992200 2201 2202 2203 2204 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22052206 2207 2208 2209 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;22102211 2212 fn collection_tokens(&self) -> Vec<TokenId>;22132214 2215 2216 2217 fn token_exists(&self, token: TokenId) -> bool;22182219 2220 fn last_token_id(&self) -> TokenId;22212222 2223 2224 2225 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22262227 2228 2229 2230 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22312232 2233 2234 2235 2236 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22372238 2239 2240 2241 2242 2243 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22442245 2246 fn total_supply(&self) -> u32;22472248 2249 2250 2251 fn account_balance(&self, account: T::CrossAccountId) -> u32;22522253 2254 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;22552256 2257 fn total_pieces(&self, token: TokenId) -> Option<u128>;22582259 2260 2261 2262 2263 2264 fn allowance(2265 &self,2266 sender: T::CrossAccountId,2267 spender: T::CrossAccountId,2268 token: TokenId,2269 ) -> u128;22702271 2272 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;22732274 2275 2276 2277 2278 fn set_allowance_for_all(2279 &self,2280 owner: T::CrossAccountId,2281 operator: T::CrossAccountId,2282 approve: bool,2283 ) -> DispatchResultWithPostInfo;22842285 2286 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;22872288 2289 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2290}229122922293pub trait RefungibleExtensions<T>2294where2295 T: Config,2296{2297 2298 2299 2300 2301 2302 2303 2304 fn repartition(2305 &self,2306 sender: &T::CrossAccountId,2307 token: TokenId,2308 amount: u128,2309 ) -> DispatchResultWithPostInfo;2310}23112312231323142315pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2316 let post_info = PostDispatchInfo {2317 actual_weight: Some(weight),2318 pays_fee: Pays::Yes,2319 };2320 match res {2321 Ok(()) => Ok(post_info),2322 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2323 }2324}23252326impl<T: Config> From<PropertiesError> for Error<T> {2327 fn from(error: PropertiesError) -> Self {2328 match error {2329 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2330 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2331 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2332 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2333 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2334 }2335 }2336}