12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#![warn(missing_docs)]53#![cfg_attr(not(feature = "std"), no_std)]54extern crate alloc;5556use core::ops::{Deref, DerefMut};57use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};58use sp_std::vec::Vec;59use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};60use evm_coder::ToLog;61use frame_support::{62 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},63 ensure,64 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},65 dispatch::Pays,66 transactional,67};68use pallet_evm::GasWeightMapping;69use up_data_structs::{70 COLLECTION_NUMBER_LIMIT,71 Collection,72 RpcCollection,73 CollectionFlags,74 RpcCollectionFlags,75 CollectionId,76 CreateItemData,77 MAX_TOKEN_PREFIX_LENGTH,78 COLLECTION_ADMINS_LIMIT,79 TokenId,80 TokenChild,81 CollectionStats,82 MAX_TOKEN_OWNERSHIP,83 CollectionMode,84 NFT_SPONSOR_TRANSFER_TIMEOUT,85 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,86 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,87 MAX_SPONSOR_TIMEOUT,88 CUSTOM_DATA_LIMIT,89 CollectionLimits,90 CreateCollectionData,91 SponsorshipState,92 CreateItemExData,93 SponsoringRateLimit,94 budget::Budget,95 PhantomType,96 Property,97 Properties,98 PropertiesPermissionMap,99 PropertyKey,100 PropertyValue,101 PropertyPermission,102 PropertiesError,103 PropertyKeyPermission,104 TokenData,105 TrySetProperty,106 PropertyScope,107 108 RmrkCollectionInfo,109 RmrkInstanceInfo,110 RmrkResourceInfo,111 RmrkPropertyInfo,112 RmrkBaseInfo,113 RmrkPartType,114 RmrkBoundedTheme,115 RmrkNftChild,116 CollectionPermissions,117};118use up_pov_estimate_rpc::PovInfo;119120pub use pallet::*;121use sp_core::H160;122use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};123#[cfg(feature = "runtime-benchmarks")]124pub mod benchmarking;125pub mod dispatch;126pub mod erc;127pub mod eth;128pub mod weights;129130131pub type SelfWeightOf<T> = <T as Config>::WeightInfo;132133134135136137138139#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]140pub struct CollectionHandle<T: Config> {141 142 pub id: CollectionId,143 collection: Collection<T::AccountId>,144 145 pub recorder: SubstrateRecorder<T>,146}147148impl<T: Config> WithRecorder<T> for CollectionHandle<T> {149 fn recorder(&self) -> &SubstrateRecorder<T> {150 &self.recorder151 }152 fn into_recorder(self) -> SubstrateRecorder<T> {153 self.recorder154 }155}156157impl<T: Config> CollectionHandle<T> {158 159 pub fn mode(&self) -> CollectionMode {160 self.mode161 }162163 164 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {165 <CollectionById<T>>::get(id).map(|collection| Self {166 id,167 collection,168 recorder: SubstrateRecorder::new(gas_limit),169 })170 }171172 173 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {174 <CollectionById<T>>::get(id).map(|collection| Self {175 id,176 collection,177 recorder,178 })179 }180181 182 183 pub fn new(id: CollectionId) -> Option<Self> {184 Self::new_with_gas_limit(id, u64::MAX)185 }186187 188 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {189 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)190 }191192 193 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {194 self.recorder195 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(196 <T as frame_system::Config>::DbWeight::get()197 .read198 .saturating_mul(reads),199 )))200 }201202 203 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {204 self.recorder205 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(206 <T as frame_system::Config>::DbWeight::get()207 .write208 .saturating_mul(writes),209 )))210 }211212 213 pub fn consume_store_reads_and_writes(214 &self,215 reads: u64,216 writes: u64,217 ) -> evm_coder::execution::Result<()> {218 let weight = <T as frame_system::Config>::DbWeight::get();219 let reads = weight.read.saturating_mul(reads);220 let writes = weight.read.saturating_mul(writes);221 self.recorder222 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(223 reads.saturating_add(writes),224 )))225 }226227 228 pub fn save(&self) -> DispatchResult {229 <CollectionById<T>>::insert(self.id, &self.collection);230 Ok(())231 }232233 234 235 236 237 238 pub fn set_sponsor(239 &mut self,240 sender: &T::CrossAccountId,241 sponsor: T::AccountId,242 ) -> DispatchResult {243 self.check_is_internal()?;244 self.check_is_owner_or_admin(sender)?;245246 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());247248 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));249 <PalletEvm<T>>::deposit_log(250 erc::CollectionHelpersEvents::CollectionChanged {251 collection_id: eth::collection_id_to_address(self.id),252 }253 .to_log(T::ContractAddress::get()),254 );255256 self.save()257 }258259 260 261 262 263 264 265 266 267 268 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {269 self.check_is_internal()?;270271 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());272273 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));274 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));275 <PalletEvm<T>>::deposit_log(276 erc::CollectionHelpersEvents::CollectionChanged {277 collection_id: eth::collection_id_to_address(self.id),278 }279 .to_log(T::ContractAddress::get()),280 );281282 self.save()283 }284285 286 287 288 289 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {290 self.check_is_internal()?;291 ensure!(292 self.collection.sponsorship.pending_sponsor() == Some(sender),293 Error::<T>::ConfirmSponsorshipFail294 );295296 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());297298 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));299 <PalletEvm<T>>::deposit_log(300 erc::CollectionHelpersEvents::CollectionChanged {301 collection_id: eth::collection_id_to_address(self.id),302 }303 .to_log(T::ContractAddress::get()),304 );305306 self.save()307 }308309 310 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {311 self.check_is_internal()?;312 self.check_is_owner_or_admin(sender)?;313314 self.collection.sponsorship = SponsorshipState::Disabled;315316 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));317 <PalletEvm<T>>::deposit_log(318 erc::CollectionHelpersEvents::CollectionChanged {319 collection_id: eth::collection_id_to_address(self.id),320 }321 .to_log(T::ContractAddress::get()),322 );323 self.save()324 }325326 327 328 329 330 pub fn force_remove_sponsor(&mut self) -> DispatchResult {331 self.check_is_internal()?;332333 self.collection.sponsorship = SponsorshipState::Disabled;334335 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));336 <PalletEvm<T>>::deposit_log(337 erc::CollectionHelpersEvents::CollectionChanged {338 collection_id: eth::collection_id_to_address(self.id),339 }340 .to_log(T::ContractAddress::get()),341 );342 self.save()343 }344345 346 347 pub fn check_is_internal(&self) -> DispatchResult {348 if self.flags.external {349 return Err(<Error<T>>::CollectionIsExternal)?;350 }351352 Ok(())353 }354355 356 357 pub fn check_is_external(&self) -> DispatchResult {358 if !self.flags.external {359 return Err(<Error<T>>::CollectionIsInternal)?;360 }361362 Ok(())363 }364}365366impl<T: Config> Deref for CollectionHandle<T> {367 type Target = Collection<T::AccountId>;368369 fn deref(&self) -> &Self::Target {370 &self.collection371 }372}373374impl<T: Config> DerefMut for CollectionHandle<T> {375 fn deref_mut(&mut self) -> &mut Self::Target {376 &mut self.collection377 }378}379380impl<T: Config> CollectionHandle<T> {381 382 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {383 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);384 Ok(())385 }386387 388 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {389 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))390 }391392 393 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {394 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);395 Ok(())396 }397398 399 400 401 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {402 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)403 }404405 406 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {407 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)408 }409410 411 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {412 ensure!(413 <Allowlist<T>>::get((self.id, user)),414 <Error<T>>::AddressNotInAllowlist415 );416 Ok(())417 }418419 420 421 422 pub fn change_owner(423 &mut self,424 caller: T::CrossAccountId,425 new_owner: T::CrossAccountId,426 ) -> DispatchResult {427 self.check_is_internal()?;428 self.check_is_owner(&caller)?;429 self.collection.owner = new_owner.as_sub().clone();430431 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(432 self.id,433 new_owner.as_sub().clone(),434 ));435 <PalletEvm<T>>::deposit_log(436 erc::CollectionHelpersEvents::CollectionChanged {437 collection_id: eth::collection_id_to_address(self.id),438 }439 .to_log(T::ContractAddress::get()),440 );441442 self.save()443 }444}445446#[frame_support::pallet]447pub mod pallet {448 use super::*;449 use dispatch::CollectionDispatch;450 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};451 use frame_system::pallet_prelude::*;452 use frame_support::traits::Currency;453 use up_data_structs::{TokenId, mapping::TokenAddressMapping};454 use scale_info::TypeInfo;455 use weights::WeightInfo;456457 #[pallet::config]458 pub trait Config:459 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo460 {461 462 type WeightInfo: WeightInfo;463464 465 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;466467 468 type Currency: Currency<Self::AccountId>;469470 471 #[pallet::constant]472 type CollectionCreationPrice: Get<473 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,474 >;475476 477 type CollectionDispatch: CollectionDispatch<Self>;478479 480 type TreasuryAccountId: Get<Self::AccountId>;481482 483 #[pallet::constant]484 type ContractAddress: Get<H160>;485486 487 type EvmTokenAddressMapping: TokenAddressMapping<H160>;488489 490 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;491 }492493 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);494495 #[pallet::pallet]496 #[pallet::storage_version(STORAGE_VERSION)]497 #[pallet::generate_store(pub(super) trait Store)]498 pub struct Pallet<T>(_);499500 #[pallet::extra_constants]501 impl<T: Config> Pallet<T> {502 503 pub fn collection_admins_limit() -> u32 {504 COLLECTION_ADMINS_LIMIT505 }506 }507508 impl<T: Config> Pallet<T> {509 510 pub fn deposit_event(event: Event<T>) {511 let event = <T as Config>::RuntimeEvent::from(event);512 let event = event.into();513 <frame_system::Pallet<T>>::deposit_event(event)514 }515 }516517 #[pallet::event]518 pub enum Event<T: Config> {519 520 CollectionCreated(521 522 CollectionId,523 524 u8,525 526 T::AccountId,527 ),528529 530 CollectionDestroyed(531 532 CollectionId,533 ),534535 536 ItemCreated(537 538 CollectionId,539 540 TokenId,541 542 T::CrossAccountId,543 544 u128,545 ),546547 548 ItemDestroyed(549 550 CollectionId,551 552 TokenId,553 554 T::CrossAccountId,555 556 u128,557 ),558559 560 Transfer(561 562 CollectionId,563 564 TokenId,565 566 T::CrossAccountId,567 568 T::CrossAccountId,569 570 u128,571 ),572573 574 Approved(575 576 CollectionId,577 578 TokenId,579 580 T::CrossAccountId,581 582 T::CrossAccountId,583 584 u128,585 ),586587 588 ApprovedForAll(589 590 CollectionId,591 592 T::CrossAccountId,593 594 T::CrossAccountId,595 596 bool,597 ),598599 600 CollectionPropertySet(601 602 CollectionId,603 604 PropertyKey,605 ),606607 608 CollectionPropertyDeleted(609 610 CollectionId,611 612 PropertyKey,613 ),614615 616 TokenPropertySet(617 618 CollectionId,619 620 TokenId,621 622 PropertyKey,623 ),624625 626 TokenPropertyDeleted(627 628 CollectionId,629 630 TokenId,631 632 PropertyKey,633 ),634635 636 PropertyPermissionSet(637 638 CollectionId,639 640 PropertyKey,641 ),642643 644 AllowListAddressAdded(645 646 CollectionId,647 648 T::CrossAccountId,649 ),650651 652 AllowListAddressRemoved(653 654 CollectionId,655 656 T::CrossAccountId,657 ),658659 660 CollectionAdminAdded(661 662 CollectionId,663 664 T::CrossAccountId,665 ),666667 668 CollectionAdminRemoved(669 670 CollectionId,671 672 T::CrossAccountId,673 ),674675 676 CollectionLimitSet(677 678 CollectionId,679 ),680681 682 CollectionOwnerChanged(683 684 CollectionId,685 686 T::AccountId,687 ),688689 690 CollectionPermissionSet(691 692 CollectionId,693 ),694695 696 CollectionSponsorSet(697 698 CollectionId,699 700 T::AccountId,701 ),702703 704 SponsorshipConfirmed(705 706 CollectionId,707 708 T::AccountId,709 ),710711 712 CollectionSponsorRemoved(713 714 CollectionId,715 ),716 }717718 #[pallet::error]719 pub enum Error<T> {720 721 CollectionNotFound,722 723 MustBeTokenOwner,724 725 NoPermission,726 727 CantDestroyNotEmptyCollection,728 729 PublicMintingNotAllowed,730 731 AddressNotInAllowlist,732733 734 CollectionNameLimitExceeded,735 736 CollectionDescriptionLimitExceeded,737 738 CollectionTokenPrefixLimitExceeded,739 740 TotalCollectionsLimitExceeded,741 742 CollectionAdminCountExceeded,743 744 CollectionLimitBoundsExceeded,745 746 OwnerPermissionsCantBeReverted,747 748 TransferNotAllowed,749 750 AccountTokenLimitExceeded,751 752 CollectionTokenLimitExceeded,753 754 MetadataFlagFrozen,755756 757 TokenNotFound,758 759 TokenValueTooLow,760 761 ApprovedValueTooLow,762 763 CantApproveMoreThanOwned,764 765 AddressIsNotEthMirror,766767 768 AddressIsZero,769770 771 UnsupportedOperation,772773 774 NotSufficientFounds,775776 777 UserIsNotAllowedToNest,778 779 SourceCollectionIsNotAllowedToNest,780781 782 CollectionFieldSizeExceeded,783784 785 NoSpaceForProperty,786787 788 PropertyLimitReached,789790 791 PropertyKeyIsTooLong,792793 794 InvalidCharacterInPropertyKey,795796 797 EmptyPropertyKey,798799 800 CollectionIsExternal,801802 803 CollectionIsInternal,804805 806 ConfirmSponsorshipFail,807808 809 UserIsNotCollectionAdmin,810 }811812 813 #[pallet::storage]814 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;815816 817 #[pallet::storage]818 pub type DestroyedCollectionCount<T> =819 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;820821 822 #[pallet::storage]823 pub type CollectionById<T> = StorageMap<824 Hasher = Blake2_128Concat,825 Key = CollectionId,826 Value = Collection<<T as frame_system::Config>::AccountId>,827 QueryKind = OptionQuery,828 >;829830 831 #[pallet::storage]832 #[pallet::getter(fn collection_properties)]833 pub type CollectionProperties<T> = StorageMap<834 Hasher = Blake2_128Concat,835 Key = CollectionId,836 Value = Properties,837 QueryKind = ValueQuery,838 OnEmpty = up_data_structs::CollectionProperties,839 >;840841 842 #[pallet::storage]843 #[pallet::getter(fn property_permissions)]844 pub type CollectionPropertyPermissions<T> = StorageMap<845 Hasher = Blake2_128Concat,846 Key = CollectionId,847 Value = PropertiesPermissionMap,848 QueryKind = ValueQuery,849 >;850851 852 #[pallet::storage]853 pub type AdminAmount<T> = StorageMap<854 Hasher = Blake2_128Concat,855 Key = CollectionId,856 Value = u32,857 QueryKind = ValueQuery,858 >;859860 861 #[pallet::storage]862 pub type IsAdmin<T: Config> = StorageNMap<863 Key = (864 Key<Blake2_128Concat, CollectionId>,865 Key<Blake2_128Concat, T::CrossAccountId>,866 ),867 Value = bool,868 QueryKind = ValueQuery,869 >;870871 872 #[pallet::storage]873 pub type Allowlist<T: Config> = StorageNMap<874 Key = (875 Key<Blake2_128Concat, CollectionId>,876 Key<Blake2_128Concat, T::CrossAccountId>,877 ),878 Value = bool,879 QueryKind = ValueQuery,880 >;881882 883 #[pallet::storage]884 pub type DummyStorageValue<T: Config> = StorageValue<885 Value = (886 CollectionStats,887 CollectionId,888 TokenId,889 TokenChild,890 PhantomType<(891 TokenData<T::CrossAccountId>,892 RpcCollection<T::AccountId>,893 894 RmrkCollectionInfo<T::AccountId>,895 RmrkInstanceInfo<T::AccountId>,896 RmrkResourceInfo,897 RmrkPropertyInfo,898 RmrkBaseInfo<T::AccountId>,899 RmrkPartType,900 RmrkBoundedTheme,901 RmrkNftChild,902 903 PovInfo,904 )>,905 ),906 QueryKind = OptionQuery,907 >;908909 #[pallet::hooks]910 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {911 fn on_runtime_upgrade() -> Weight {912 StorageVersion::new(1).put::<Pallet<T>>();913914 Weight::zero()915 }916 }917}918919impl<T: Config> Pallet<T> {920 921 922 923 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {924 ensure!(925 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,926 <Error<T>>::AddressIsZero927 );928 Ok(())929 }930931 932 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {933 <IsAdmin<T>>::iter_prefix((collection,))934 .map(|(a, _)| a)935 .collect()936 }937938 939 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {940 <Allowlist<T>>::iter_prefix((collection,))941 .map(|(a, _)| a)942 .collect()943 }944945 946 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {947 <Allowlist<T>>::get((collection, user))948 }949950 951 pub fn collection_stats() -> CollectionStats {952 let created = <CreatedCollectionCount<T>>::get();953 let destroyed = <DestroyedCollectionCount<T>>::get();954 CollectionStats {955 created: created.0,956 destroyed: destroyed.0,957 alive: created.0 - destroyed.0,958 }959 }960961 962 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {963 let collection = <CollectionById<T>>::get(collection)?;964 let limits = collection.limits;965 let effective_limits = CollectionLimits {966 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),967 sponsored_data_size: Some(limits.sponsored_data_size()),968 sponsored_data_rate_limit: Some(969 limits970 .sponsored_data_rate_limit971 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),972 ),973 token_limit: Some(limits.token_limit()),974 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(975 match collection.mode {976 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,977 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,978 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,979 },980 )),981 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),982 owner_can_transfer: Some(limits.owner_can_transfer()),983 owner_can_destroy: Some(limits.owner_can_destroy()),984 transfers_enabled: Some(limits.transfers_enabled()),985 };986987 Some(effective_limits)988 }989990 991 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {992 let Collection {993 name,994 description,995 owner,996 mode,997 token_prefix,998 sponsorship,999 limits,1000 permissions,1001 flags,1002 } = <CollectionById<T>>::get(collection)?;10031004 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)1005 .into_iter()1006 .map(|(key, permission)| PropertyKeyPermission { key, permission })1007 .collect();10081009 let properties = <CollectionProperties<T>>::get(collection)1010 .into_iter()1011 .map(|(key, value)| Property { key, value })1012 .collect();10131014 let permissions = CollectionPermissions {1015 access: Some(permissions.access()),1016 mint_mode: Some(permissions.mint_mode()),1017 nesting: Some(permissions.nesting().clone()),1018 };10191020 Some(RpcCollection {1021 name: name.into_inner(),1022 description: description.into_inner(),1023 owner,1024 mode,1025 token_prefix: token_prefix.into_inner(),1026 sponsorship,1027 limits,1028 permissions,1029 token_property_permissions,1030 properties,1031 read_only: flags.external,10321033 flags: RpcCollectionFlags {1034 foreign: flags.foreign,1035 erc721metadata: flags.erc721metadata,1036 },1037 })1038 }1039}10401041macro_rules! limit_default {1042 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1043 $(1044 if let Some($new) = $new.$field {1045 let $old = $old.$field($($arg)?);1046 let _ = $new;1047 let _ = $old;1048 $check1049 } else {1050 $new.$field = $old.$field1051 }1052 )*1053 }};1054}1055macro_rules! limit_default_clone {1056 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1057 $(1058 if let Some($new) = $new.$field.clone() {1059 let $old = $old.$field($($arg)?);1060 let _ = $new;1061 let _ = $old;1062 $check1063 } else {1064 $new.$field = $old.$field.clone()1065 }1066 )*1067 }};1068}10691070impl<T: Config> Pallet<T> {1071 1072 1073 1074 1075 1076 pub fn init_collection(1077 owner: T::CrossAccountId,1078 payer: T::CrossAccountId,1079 data: CreateCollectionData<T::AccountId>,1080 flags: CollectionFlags,1081 ) -> Result<CollectionId, DispatchError> {1082 {1083 ensure!(1084 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1085 Error::<T>::CollectionTokenPrefixLimitExceeded1086 );1087 }10881089 let created_count = <CreatedCollectionCount<T>>::get()1090 .01091 .checked_add(1)1092 .ok_or(ArithmeticError::Overflow)?;1093 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1094 let id = CollectionId(created_count);10951096 1097 ensure!(1098 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1099 <Error<T>>::TotalCollectionsLimitExceeded1100 );11011102 11031104 let collection = Collection {1105 owner: owner.as_sub().clone(),1106 name: data.name,1107 mode: data.mode.clone(),1108 description: data.description,1109 token_prefix: data.token_prefix,1110 sponsorship: data1111 .pending_sponsor1112 .map(SponsorshipState::Unconfirmed)1113 .unwrap_or_default(),1114 limits: data1115 .limits1116 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1117 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1118 permissions: data1119 .permissions1120 .map(|permissions| {1121 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1122 })1123 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1124 flags,1125 };11261127 let mut collection_properties = up_data_structs::CollectionProperties::get();1128 collection_properties1129 .try_set_from_iter(data.properties.into_iter())1130 .map_err(<Error<T>>::from)?;11311132 CollectionProperties::<T>::insert(id, collection_properties);11331134 let mut token_props_permissions = PropertiesPermissionMap::new();1135 token_props_permissions1136 .try_set_from_iter(data.token_property_permissions.into_iter())1137 .map_err(<Error<T>>::from)?;11381139 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11401141 1142 {1143 let mut imbalance =1144 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1145 imbalance.subsume(1146 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1147 &T::TreasuryAccountId::get(),1148 T::CollectionCreationPrice::get(),1149 ),1150 );1151 <T as Config>::Currency::settle(1152 payer.as_sub(),1153 imbalance,1154 WithdrawReasons::TRANSFER,1155 ExistenceRequirement::KeepAlive,1156 )1157 .map_err(|_| Error::<T>::NotSufficientFounds)?;1158 }11591160 <CreatedCollectionCount<T>>::put(created_count);1161 <Pallet<T>>::deposit_event(Event::CollectionCreated(1162 id,1163 data.mode.id(),1164 owner.as_sub().clone(),1165 ));1166 <PalletEvm<T>>::deposit_log(1167 erc::CollectionHelpersEvents::CollectionCreated {1168 owner: *owner.as_eth(),1169 collection_id: eth::collection_id_to_address(id),1170 }1171 .to_log(T::ContractAddress::get()),1172 );1173 <CollectionById<T>>::insert(id, collection);1174 Ok(id)1175 }11761177 1178 1179 1180 1181 pub fn destroy_collection(1182 collection: CollectionHandle<T>,1183 sender: &T::CrossAccountId,1184 ) -> DispatchResult {1185 ensure!(1186 collection.limits.owner_can_destroy(),1187 <Error<T>>::NoPermission,1188 );1189 collection.check_is_owner(sender)?;11901191 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1192 .01193 .checked_add(1)1194 .ok_or(ArithmeticError::Overflow)?;11951196 11971198 <DestroyedCollectionCount<T>>::put(destroyed_collections);1199 <CollectionById<T>>::remove(collection.id);1200 <AdminAmount<T>>::remove(collection.id);1201 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1202 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1203 <CollectionProperties<T>>::remove(collection.id);12041205 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));12061207 <PalletEvm<T>>::deposit_log(1208 erc::CollectionHelpersEvents::CollectionDestroyed {1209 collection_id: eth::collection_id_to_address(collection.id),1210 }1211 .to_log(T::ContractAddress::get()),1212 );1213 Ok(())1214 }12151216 1217 1218 1219 1220 1221 1222 1223 1224 #[transactional]1225 fn modify_collection_properties(1226 collection: &CollectionHandle<T>,1227 sender: &T::CrossAccountId,1228 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1229 ) -> DispatchResult {1230 collection.check_is_owner_or_admin(sender)?;12311232 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12331234 for (key, value) in properties_updates {1235 match value {1236 Some(value) => {1237 stored_properties1238 .try_set(key.clone(), value)1239 .map_err(<Error<T>>::from)?;12401241 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1242 <PalletEvm<T>>::deposit_log(1243 erc::CollectionHelpersEvents::CollectionChanged {1244 collection_id: eth::collection_id_to_address(collection.id),1245 }1246 .to_log(T::ContractAddress::get()),1247 );1248 }1249 None => {1250 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12511252 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1253 <PalletEvm<T>>::deposit_log(1254 erc::CollectionHelpersEvents::CollectionChanged {1255 collection_id: eth::collection_id_to_address(collection.id),1256 }1257 .to_log(T::ContractAddress::get()),1258 );1259 }1260 }1261 }12621263 <CollectionProperties<T>>::set(collection.id, stored_properties);12641265 Ok(())1266 }12671268 1269 1270 1271 1272 1273 pub fn set_collection_property(1274 collection: &CollectionHandle<T>,1275 sender: &T::CrossAccountId,1276 property: Property,1277 ) -> DispatchResult {1278 Self::set_collection_properties(collection, sender, [property].into_iter())1279 }12801281 1282 1283 1284 1285 1286 1287 pub fn set_scoped_collection_property(1288 collection_id: CollectionId,1289 scope: PropertyScope,1290 property: Property,1291 ) -> DispatchResult {1292 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1293 properties.try_scoped_set(scope, property.key, property.value)1294 })1295 .map_err(<Error<T>>::from)?;12961297 Ok(())1298 }12991300 1301 1302 1303 1304 1305 1306 pub fn set_scoped_collection_properties(1307 collection_id: CollectionId,1308 scope: PropertyScope,1309 properties: impl Iterator<Item = Property>,1310 ) -> DispatchResult {1311 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1312 stored_properties.try_scoped_set_from_iter(scope, properties)1313 })1314 .map_err(<Error<T>>::from)?;13151316 Ok(())1317 }13181319 1320 1321 1322 1323 1324 pub fn set_collection_properties(1325 collection: &CollectionHandle<T>,1326 sender: &T::CrossAccountId,1327 properties: impl Iterator<Item = Property>,1328 ) -> DispatchResult {1329 Self::modify_collection_properties(1330 collection,1331 sender,1332 properties.map(|property| (property.key, Some(property.value))),1333 )1334 }13351336 1337 1338 1339 1340 1341 pub fn delete_collection_property(1342 collection: &CollectionHandle<T>,1343 sender: &T::CrossAccountId,1344 property_key: PropertyKey,1345 ) -> DispatchResult {1346 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1347 }13481349 1350 1351 1352 1353 1354 pub fn delete_collection_properties(1355 collection: &CollectionHandle<T>,1356 sender: &T::CrossAccountId,1357 property_keys: impl Iterator<Item = PropertyKey>,1358 ) -> DispatchResult {1359 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1360 }13611362 1363 1364 1365 1366 1367 1368 pub fn set_property_permission_unchecked(1369 collection: CollectionId,1370 property_permission: PropertyKeyPermission,1371 ) -> DispatchResult {1372 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1373 permissions.try_set(property_permission.key, property_permission.permission)1374 })1375 .map_err(<Error<T>>::from)?;1376 Ok(())1377 }13781379 1380 1381 1382 1383 1384 pub fn set_property_permission(1385 collection: &CollectionHandle<T>,1386 sender: &T::CrossAccountId,1387 property_permission: PropertyKeyPermission,1388 ) -> DispatchResult {1389 Self::set_scoped_property_permission(1390 collection,1391 sender,1392 PropertyScope::None,1393 property_permission,1394 )1395 }13961397 1398 1399 1400 1401 1402 1403 pub fn set_scoped_property_permission(1404 collection: &CollectionHandle<T>,1405 sender: &T::CrossAccountId,1406 scope: PropertyScope,1407 property_permission: PropertyKeyPermission,1408 ) -> DispatchResult {1409 collection.check_is_owner_or_admin(sender)?;14101411 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1412 let current_permission = all_permissions.get(&property_permission.key);1413 if matches![1414 current_permission,1415 Some(PropertyPermission { mutable: false, .. })1416 ] {1417 return Err(<Error<T>>::NoPermission.into());1418 }14191420 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1421 let property_permission = property_permission.clone();1422 permissions.try_scoped_set(1423 scope,1424 property_permission.key,1425 property_permission.permission,1426 )1427 })1428 .map_err(<Error<T>>::from)?;14291430 Self::deposit_event(Event::PropertyPermissionSet(1431 collection.id,1432 property_permission.key,1433 ));1434 <PalletEvm<T>>::deposit_log(1435 erc::CollectionHelpersEvents::CollectionChanged {1436 collection_id: eth::collection_id_to_address(collection.id),1437 }1438 .to_log(T::ContractAddress::get()),1439 );14401441 Ok(())1442 }14431444 1445 1446 1447 1448 1449 #[transactional]1450 pub fn set_token_property_permissions(1451 collection: &CollectionHandle<T>,1452 sender: &T::CrossAccountId,1453 property_permissions: Vec<PropertyKeyPermission>,1454 ) -> DispatchResult {1455 Self::set_scoped_token_property_permissions(1456 collection,1457 sender,1458 PropertyScope::None,1459 property_permissions,1460 )1461 }14621463 1464 1465 1466 1467 1468 1469 #[transactional]1470 pub fn set_scoped_token_property_permissions(1471 collection: &CollectionHandle<T>,1472 sender: &T::CrossAccountId,1473 scope: PropertyScope,1474 property_permissions: Vec<PropertyKeyPermission>,1475 ) -> DispatchResult {1476 for prop_pemission in property_permissions {1477 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1478 }14791480 Ok(())1481 }14821483 1484 pub fn get_collection_property(1485 collection_id: CollectionId,1486 key: &PropertyKey,1487 ) -> Option<PropertyValue> {1488 Self::collection_properties(collection_id).get(key).cloned()1489 }14901491 1492 pub fn bytes_keys_to_property_keys(1493 keys: Vec<Vec<u8>>,1494 ) -> Result<Vec<PropertyKey>, DispatchError> {1495 keys.into_iter()1496 .map(|key| -> Result<PropertyKey, DispatchError> {1497 key.try_into()1498 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1499 })1500 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1501 }15021503 1504 pub fn filter_collection_properties(1505 collection_id: CollectionId,1506 keys: Option<Vec<PropertyKey>>,1507 ) -> Result<Vec<Property>, DispatchError> {1508 let properties = Self::collection_properties(collection_id);15091510 let properties = keys1511 .map(|keys| {1512 keys.into_iter()1513 .filter_map(|key| {1514 properties.get(&key).map(|value| Property {1515 key,1516 value: value.clone(),1517 })1518 })1519 .collect()1520 })1521 .unwrap_or_else(|| {1522 properties1523 .into_iter()1524 .map(|(key, value)| Property { key, value })1525 .collect()1526 });15271528 Ok(properties)1529 }15301531 1532 pub fn filter_property_permissions(1533 collection_id: CollectionId,1534 keys: Option<Vec<PropertyKey>>,1535 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1536 let permissions = Self::property_permissions(collection_id);15371538 let key_permissions = keys1539 .map(|keys| {1540 keys.into_iter()1541 .filter_map(|key| {1542 permissions1543 .get(&key)1544 .map(|permission| PropertyKeyPermission {1545 key,1546 permission: permission.clone(),1547 })1548 })1549 .collect()1550 })1551 .unwrap_or_else(|| {1552 permissions1553 .into_iter()1554 .map(|(key, permission)| PropertyKeyPermission { key, permission })1555 .collect()1556 });15571558 Ok(key_permissions)1559 }15601561 1562 1563 1564 pub fn toggle_allowlist(1565 collection: &CollectionHandle<T>,1566 sender: &T::CrossAccountId,1567 user: &T::CrossAccountId,1568 allowed: bool,1569 ) -> DispatchResult {1570 collection.check_is_owner_or_admin(sender)?;15711572 15731574 if allowed {1575 <Allowlist<T>>::insert((collection.id, user), true);1576 Self::deposit_event(Event::<T>::AllowListAddressAdded(1577 collection.id,1578 user.clone(),1579 ));1580 } else {1581 <Allowlist<T>>::remove((collection.id, user));1582 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1583 collection.id,1584 user.clone(),1585 ));1586 }15871588 <PalletEvm<T>>::deposit_log(1589 erc::CollectionHelpersEvents::CollectionChanged {1590 collection_id: eth::collection_id_to_address(collection.id),1591 }1592 .to_log(T::ContractAddress::get()),1593 );15941595 Ok(())1596 }15971598 1599 1600 1601 pub fn toggle_admin(1602 collection: &CollectionHandle<T>,1603 sender: &T::CrossAccountId,1604 user: &T::CrossAccountId,1605 admin: bool,1606 ) -> DispatchResult {1607 collection.check_is_internal()?;1608 collection.check_is_owner(sender)?;16091610 let is_admin = <IsAdmin<T>>::get((collection.id, user));1611 if is_admin == admin {1612 if admin {1613 return Ok(());1614 } else {1615 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1616 }1617 }1618 let amount = <AdminAmount<T>>::get(collection.id);16191620 16211622 if admin {1623 let amount = amount1624 .checked_add(1)1625 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1626 ensure!(1627 amount <= Self::collection_admins_limit(),1628 <Error<T>>::CollectionAdminCountExceeded,1629 );16301631 <AdminAmount<T>>::insert(collection.id, amount);1632 <IsAdmin<T>>::insert((collection.id, user), true);16331634 Self::deposit_event(Event::<T>::CollectionAdminAdded(1635 collection.id,1636 user.clone(),1637 ));1638 } else {1639 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1640 <IsAdmin<T>>::remove((collection.id, user));16411642 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1643 collection.id,1644 user.clone(),1645 ));1646 }16471648 <PalletEvm<T>>::deposit_log(1649 erc::CollectionHelpersEvents::CollectionChanged {1650 collection_id: eth::collection_id_to_address(collection.id),1651 }1652 .to_log(T::ContractAddress::get()),1653 );16541655 Ok(())1656 }16571658 1659 pub fn update_limits(1660 user: &T::CrossAccountId,1661 collection: &mut CollectionHandle<T>,1662 new_limit: CollectionLimits,1663 ) -> DispatchResult {1664 collection.check_is_internal()?;1665 collection.check_is_owner_or_admin(user)?;16661667 collection.limits =1668 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16691670 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1671 <PalletEvm<T>>::deposit_log(1672 erc::CollectionHelpersEvents::CollectionChanged {1673 collection_id: eth::collection_id_to_address(collection.id),1674 }1675 .to_log(T::ContractAddress::get()),1676 );16771678 collection.save()1679 }16801681 1682 fn clamp_limits(1683 mode: CollectionMode,1684 old_limit: &CollectionLimits,1685 mut new_limit: CollectionLimits,1686 ) -> Result<CollectionLimits, DispatchError> {1687 let limits = old_limit;1688 limit_default!(old_limit, new_limit,1689 account_token_ownership_limit => ensure!(1690 new_limit <= MAX_TOKEN_OWNERSHIP,1691 <Error<T>>::CollectionLimitBoundsExceeded,1692 ),1693 sponsored_data_size => ensure!(1694 new_limit <= CUSTOM_DATA_LIMIT,1695 <Error<T>>::CollectionLimitBoundsExceeded,1696 ),16971698 sponsored_data_rate_limit => {},1699 token_limit => ensure!(1700 old_limit >= new_limit && new_limit > 0,1701 <Error<T>>::CollectionTokenLimitExceeded1702 ),17031704 sponsor_transfer_timeout(match mode {1705 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1706 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1707 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1708 }) => ensure!(1709 new_limit <= MAX_SPONSOR_TIMEOUT,1710 <Error<T>>::CollectionLimitBoundsExceeded,1711 ),1712 sponsor_approve_timeout => {},1713 owner_can_transfer => ensure!(1714 !limits.owner_can_transfer_instaled() ||1715 old_limit || !new_limit,1716 <Error<T>>::OwnerPermissionsCantBeReverted,1717 ),1718 owner_can_destroy => ensure!(1719 old_limit || !new_limit,1720 <Error<T>>::OwnerPermissionsCantBeReverted,1721 ),1722 transfers_enabled => {},1723 );1724 Ok(new_limit)1725 }17261727 1728 pub fn update_permissions(1729 user: &T::CrossAccountId,1730 collection: &mut CollectionHandle<T>,1731 new_permission: CollectionPermissions,1732 ) -> DispatchResult {1733 collection.check_is_internal()?;1734 collection.check_is_owner_or_admin(user)?;1735 collection.permissions = Self::clamp_permissions(1736 collection.mode.clone(),1737 &collection.permissions,1738 new_permission,1739 )?;17401741 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1742 <PalletEvm<T>>::deposit_log(1743 erc::CollectionHelpersEvents::CollectionChanged {1744 collection_id: eth::collection_id_to_address(collection.id),1745 }1746 .to_log(T::ContractAddress::get()),1747 );17481749 collection.save()1750 }17511752 1753 fn clamp_permissions(1754 _mode: CollectionMode,1755 old_permission: &CollectionPermissions,1756 mut new_permission: CollectionPermissions,1757 ) -> Result<CollectionPermissions, DispatchError> {1758 limit_default_clone!(old_permission, new_permission,1759 access => {},1760 mint_mode => {},1761 nesting => { },1762 );1763 Ok(new_permission)1764 }17651766 1767 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1768 CollectionProperties::<T>::mutate(collection_id, |properties| {1769 properties.recompute_consumed_space();1770 });17711772 Ok(())1773 }1774}177517761777#[macro_export]1778macro_rules! unsupported {1779 ($runtime:path) => {1780 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1781 };1782}178317841785pub trait CommonWeightInfo<CrossAccountId> {1786 1787 fn create_item() -> Weight;17881789 1790 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17911792 1793 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17941795 1796 fn burn_item() -> Weight;17971798 1799 1800 1801 fn set_collection_properties(amount: u32) -> Weight;18021803 1804 1805 1806 fn delete_collection_properties(amount: u32) -> Weight;18071808 1809 1810 1811 fn set_token_properties(amount: u32) -> Weight;18121813 1814 1815 1816 fn delete_token_properties(amount: u32) -> Weight;18171818 1819 1820 1821 fn set_token_property_permissions(amount: u32) -> Weight;18221823 1824 fn transfer() -> Weight;18251826 1827 fn approve() -> Weight;18281829 1830 fn approve_from() -> Weight;18311832 1833 fn transfer_from() -> Weight;18341835 1836 fn burn_from() -> Weight;18371838 1839 1840 1841 1842 fn burn_recursively_self_raw() -> Weight;18431844 1845 1846 1847 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18481849 1850 1851 1852 1853 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1854 Self::burn_recursively_self_raw()1855 .saturating_mul(max_selfs.max(1) as u64)1856 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1857 }18581859 1860 fn token_owner() -> Weight;18611862 1863 fn set_allowance_for_all() -> Weight;18641865 1866 fn force_repair_item() -> Weight;1867}186818691870pub trait RefungibleExtensionsWeightInfo {1871 1872 fn repartition() -> Weight;1873}187418751876187718781879pub trait CommonCollectionOperations<T: Config> {1880 1881 fn mode(&self) -> CollectionMode;18821883 1884 1885 1886 1887 1888 1889 fn create_item(1890 &self,1891 sender: T::CrossAccountId,1892 to: T::CrossAccountId,1893 data: CreateItemData,1894 nesting_budget: &dyn Budget,1895 ) -> DispatchResultWithPostInfo;18961897 1898 1899 1900 1901 1902 1903 fn create_multiple_items(1904 &self,1905 sender: T::CrossAccountId,1906 to: T::CrossAccountId,1907 data: Vec<CreateItemData>,1908 nesting_budget: &dyn Budget,1909 ) -> DispatchResultWithPostInfo;19101911 1912 1913 1914 1915 1916 1917 fn create_multiple_items_ex(1918 &self,1919 sender: T::CrossAccountId,1920 data: CreateItemExData<T::CrossAccountId>,1921 nesting_budget: &dyn Budget,1922 ) -> DispatchResultWithPostInfo;19231924 1925 1926 1927 1928 1929 fn burn_item(1930 &self,1931 sender: T::CrossAccountId,1932 token: TokenId,1933 amount: u128,1934 ) -> DispatchResultWithPostInfo;19351936 1937 1938 1939 1940 1941 1942 fn burn_item_recursively(1943 &self,1944 sender: T::CrossAccountId,1945 token: TokenId,1946 self_budget: &dyn Budget,1947 breadth_budget: &dyn Budget,1948 ) -> DispatchResultWithPostInfo;19491950 1951 1952 1953 1954 fn set_collection_properties(1955 &self,1956 sender: T::CrossAccountId,1957 properties: Vec<Property>,1958 ) -> DispatchResultWithPostInfo;19591960 1961 1962 1963 1964 fn delete_collection_properties(1965 &self,1966 sender: &T::CrossAccountId,1967 property_keys: Vec<PropertyKey>,1968 ) -> DispatchResultWithPostInfo;19691970 1971 1972 1973 1974 1975 1976 1977 1978 1979 fn set_token_properties(1980 &self,1981 sender: T::CrossAccountId,1982 token_id: TokenId,1983 properties: Vec<Property>,1984 budget: &dyn Budget,1985 ) -> DispatchResultWithPostInfo;19861987 1988 1989 1990 1991 1992 1993 1994 1995 1996 fn delete_token_properties(1997 &self,1998 sender: T::CrossAccountId,1999 token_id: TokenId,2000 property_keys: Vec<PropertyKey>,2001 budget: &dyn Budget,2002 ) -> DispatchResultWithPostInfo;20032004 2005 2006 2007 2008 2009 2010 fn set_token_property_permissions(2011 &self,2012 sender: &T::CrossAccountId,2013 property_permissions: Vec<PropertyKeyPermission>,2014 ) -> DispatchResultWithPostInfo;20152016 2017 2018 2019 2020 2021 2022 2023 fn transfer(2024 &self,2025 sender: T::CrossAccountId,2026 to: T::CrossAccountId,2027 token: TokenId,2028 amount: u128,2029 budget: &dyn Budget,2030 ) -> DispatchResultWithPostInfo;20312032 2033 2034 2035 2036 2037 2038 fn approve(2039 &self,2040 sender: T::CrossAccountId,2041 spender: T::CrossAccountId,2042 token: TokenId,2043 amount: u128,2044 ) -> DispatchResultWithPostInfo;20452046 2047 2048 2049 2050 2051 2052 2053 fn approve_from(2054 &self,2055 sender: T::CrossAccountId,2056 from: T::CrossAccountId,2057 to: T::CrossAccountId,2058 token: TokenId,2059 amount: u128,2060 ) -> DispatchResultWithPostInfo;20612062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 fn transfer_from(2073 &self,2074 sender: T::CrossAccountId,2075 from: T::CrossAccountId,2076 to: T::CrossAccountId,2077 token: TokenId,2078 amount: u128,2079 budget: &dyn Budget,2080 ) -> DispatchResultWithPostInfo;20812082 2083 2084 2085 2086 2087 2088 2089 2090 2091 fn burn_from(2092 &self,2093 sender: T::CrossAccountId,2094 from: T::CrossAccountId,2095 token: TokenId,2096 amount: u128,2097 budget: &dyn Budget,2098 ) -> DispatchResultWithPostInfo;20992100 2101 2102 2103 2104 2105 2106 fn check_nesting(2107 &self,2108 sender: T::CrossAccountId,2109 from: (CollectionId, TokenId),2110 under: TokenId,2111 budget: &dyn Budget,2112 ) -> DispatchResult;21132114 2115 2116 2117 2118 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21192120 2121 2122 2123 2124 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21252126 2127 2128 2129 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21302131 2132 fn collection_tokens(&self) -> Vec<TokenId>;21332134 2135 2136 2137 fn token_exists(&self, token: TokenId) -> bool;21382139 2140 fn last_token_id(&self) -> TokenId;21412142 2143 2144 2145 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;21462147 2148 2149 2150 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;21512152 2153 2154 2155 2156 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21572158 2159 2160 2161 2162 2163 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21642165 2166 fn total_supply(&self) -> u32;21672168 2169 2170 2171 fn account_balance(&self, account: T::CrossAccountId) -> u32;21722173 2174 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21752176 2177 fn total_pieces(&self, token: TokenId) -> Option<u128>;21782179 2180 2181 2182 2183 2184 fn allowance(2185 &self,2186 sender: T::CrossAccountId,2187 spender: T::CrossAccountId,2188 token: TokenId,2189 ) -> u128;21902191 2192 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21932194 2195 2196 2197 2198 fn set_allowance_for_all(2199 &self,2200 owner: T::CrossAccountId,2201 operator: T::CrossAccountId,2202 approve: bool,2203 ) -> DispatchResultWithPostInfo;22042205 2206 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;22072208 2209 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2210}221122122213pub trait RefungibleExtensions<T>2214where2215 T: Config,2216{2217 2218 2219 2220 2221 2222 2223 2224 fn repartition(2225 &self,2226 sender: &T::CrossAccountId,2227 token: TokenId,2228 amount: u128,2229 ) -> DispatchResultWithPostInfo;2230}22312232223322342235pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2236 let post_info = PostDispatchInfo {2237 actual_weight: Some(weight),2238 pays_fee: Pays::Yes,2239 };2240 match res {2241 Ok(()) => Ok(post_info),2242 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2243 }2244}22452246impl<T: Config> From<PropertiesError> for Error<T> {2247 fn from(error: PropertiesError) -> Self {2248 match error {2249 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2250 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2251 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2252 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2253 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2254 }2255 }2256}