1234567891011121314151617#![recursion_limit = "1024"]18#![cfg_attr(not(feature = "std"), no_std)]19#![allow(20 clippy::too_many_arguments,21 clippy::unnecessary_mut_passed,22 clippy::unused_unit23)]2425use frame_support::{26 decl_module, decl_storage, decl_error, decl_event,27 dispatch::DispatchResult,28 ensure,29 weights::{Weight},30 transactional,31 pallet_prelude::{DispatchResultWithPostInfo, ConstU32},32 BoundedVec,33};34use scale_info::TypeInfo;35use frame_system::{self as system, ensure_signed};36use sp_runtime::{sp_std::prelude::Vec};37use up_data_structs::{38 MAX_COLLECTION_NAME_LENGTH,39 MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData,40 CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId, SponsorshipState,41 CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,42 PropertyKeyPermission,43};44use pallet_evm::account::CrossAccountId;45use pallet_common::{46 CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_call,47 dispatch::CollectionDispatch,48};4950#[cfg(feature = "runtime-benchmarks")]51mod benchmarking;52pub mod weights;53use weights::WeightInfo;5455const NESTING_BUDGET: u32 = 5;5657decl_error! {58 59 pub enum Error for Module<T: Config> {60 61 CollectionDecimalPointLimitExceeded,62 63 ConfirmUnsetSponsorFail,64 65 EmptyArgument,66 }67}6869pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {70 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7172 73 type WeightInfo: WeightInfo;74 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;75}7677decl_event! {78 pub enum Event<T>79 where80 <T as frame_system::Config>::AccountId,81 <T as pallet_evm::account::Config>::CrossAccountId,82 {83 84 85 86 87 88 CollectionSponsorRemoved(CollectionId),8990 91 92 93 94 95 96 97 CollectionAdminAdded(CollectionId, CrossAccountId),9899 100 101 102 103 104 105 106 CollectionOwnedChanged(CollectionId, AccountId),107108 109 110 111 112 113 114 115 CollectionSponsorSet(CollectionId, AccountId),116117 118 119 120 121 122 ConstOnChainSchemaSet(CollectionId),123124 125 126 127 128 129 130 131 SponsorshipConfirmed(CollectionId, AccountId),132133 134 135 136 137 138 139 140 CollectionAdminRemoved(CollectionId, CrossAccountId),141142 143 144 145 146 147 148 149 AllowListAddressRemoved(CollectionId, CrossAccountId),150151 152 153 154 155 156 157 158 AllowListAddressAdded(CollectionId, CrossAccountId),159160 161 162 163 164 165 CollectionLimitSet(CollectionId),166167 CollectionPermissionSet(CollectionId),168169 170 171 172 173 174 MintPermissionSet(CollectionId),175176 177 178 179 180 181 OffchainSchemaSet(CollectionId),182183 184 185 186 187 188 189 190 PublicAccessModeSet(CollectionId, AccessMode),191192 193 194 195 196 197 SchemaVersionSet(CollectionId),198 }199}200201type SelfWeightOf<T> = <T as Config>::WeightInfo;202203204205206207208209210211212213214215216217218219220221222223224225decl_storage! {226 trait Store for Module<T: Config> as Unique {227228 229 230 ChainVersion: u64;231 232233 234 235 236 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;237 238 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;239 240 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;241 242 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;243 244245 246 247 #[deprecated]248 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;249 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;250251 252 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;253 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;254 pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;255 }256}257258decl_module! {259 pub struct Module<T: Config> for enum Call260 where261 origin: T::Origin262 {263 type Error = Error<T>;264265 fn deposit_event() = default;266267 fn on_initialize(_now: T::BlockNumber) -> Weight {268 0269 }270271 fn on_runtime_upgrade() -> Weight {272 let limit = None;273274 <VariableMetaDataBasket<T>>::remove_all(limit);275276 0277 }278279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 #[weight = <SelfWeightOf<T>>::create_collection()]296 #[transactional]297 #[deprecated]298 pub fn create_collection(origin,299 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,300 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,301 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,302 mode: CollectionMode) -> DispatchResult {303 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {304 name: collection_name,305 description: collection_description,306 token_prefix,307 mode,308 ..Default::default()309 };310 Self::create_collection_ex(origin, data)311 }312313 314 315 316 #[weight = <SelfWeightOf<T>>::create_collection()]317 #[transactional]318 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {319 let sender = ensure_signed(origin)?;320321 322323 T::CollectionDispatch::create(sender, data)?;324325 Ok(())326 }327328 329 330 331 332 333 334 335 336 337 #[weight = <SelfWeightOf<T>>::destroy_collection()]338 #[transactional]339 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {340 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);341 let collection = <CollectionHandle<T>>::try_get(collection_id)?;342343 344345 T::CollectionDispatch::destroy(sender, collection)?;346347 <NftTransferBasket<T>>::remove_prefix(collection_id, None);348 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);349 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);350351 <NftApproveBasket<T>>::remove_prefix(collection_id, None);352 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);353 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);354355 Ok(())356 }357358 359 360 361 362 363 364 365 366 367 368 369 370 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]371 #[transactional]372 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{373374 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);375 let collection = <CollectionHandle<T>>::try_get(collection_id)?;376377 <PalletCommon<T>>::toggle_allowlist(378 &collection,379 &sender,380 &address,381 true,382 )?;383384 Self::deposit_event(Event::<T>::AllowListAddressAdded(385 collection_id,386 address387 ));388389 Ok(())390 }391392 393 394 395 396 397 398 399 400 401 402 403 404 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]405 #[transactional]406 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{407408 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);409 let collection = <CollectionHandle<T>>::try_get(collection_id)?;410411 <PalletCommon<T>>::toggle_allowlist(412 &collection,413 &sender,414 &address,415 false,416 )?;417418 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(419 collection_id,420 address421 ));422423 Ok(())424 }425426 427 428 429 430 431 432 433 434 435 436 437 #[weight = <SelfWeightOf<T>>::change_collection_owner()]438 #[transactional]439 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {440441 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);442443 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;444 target_collection.check_is_owner(&sender)?;445446 target_collection.owner = new_owner.clone();447 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(448 collection_id,449 new_owner450 ));451452 target_collection.save()453 }454455 456 457 458 459 460 461 462 463 464 465 466 467 468 #[weight = <SelfWeightOf<T>>::add_collection_admin()]469 #[transactional]470 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {471 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);472 let collection = <CollectionHandle<T>>::try_get(collection_id)?;473474 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(475 collection_id,476 new_admin_id.clone()477 ));478479 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)480 }481482 483 484 485 486 487 488 489 490 491 492 493 494 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]495 #[transactional]496 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {497 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);498 let collection = <CollectionHandle<T>>::try_get(collection_id)?;499500 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(501 collection_id,502 account_id.clone()503 ));504505 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)506 }507508 509 510 511 512 513 514 515 516 517 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]518 #[transactional]519 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {520 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);521522 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;523 target_collection.check_is_owner(&sender)?;524525 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());526527 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(528 collection_id,529 new_sponsor530 ));531532 target_collection.save()533 }534535 536 537 538 539 540 541 542 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]543 #[transactional]544 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {545 let sender = ensure_signed(origin)?;546547 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;548 ensure!(549 target_collection.sponsorship.pending_sponsor() == Some(&sender),550 Error::<T>::ConfirmUnsetSponsorFail551 );552553 target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());554555 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(556 collection_id,557 sender558 ));559560 target_collection.save()561 }562563 564 565 566 567 568 569 570 571 572 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]573 #[transactional]574 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {575 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);576577 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;578 target_collection.check_is_owner(&sender)?;579580 target_collection.sponsorship = SponsorshipState::Disabled;581582 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(583 collection_id584 ));585 target_collection.save()586 }587588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 #[weight = T::CommonWeightInfo::create_item()]607 #[transactional]608 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {609 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);610 let budget = budget::Value::new(NESTING_BUDGET);611612 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))613 }614615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]634 #[transactional]635 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {636 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);637 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);638 let budget = budget::Value::new(NESTING_BUDGET);639640 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))641 }642643 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]644 #[transactional]645 pub fn set_collection_properties(646 origin,647 collection_id: CollectionId,648 properties: Vec<Property>649 ) -> DispatchResultWithPostInfo {650 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);651652 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);653654 dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))655 }656657 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]658 #[transactional]659 pub fn delete_collection_properties(660 origin,661 collection_id: CollectionId,662 property_keys: Vec<PropertyKey>,663 ) -> DispatchResultWithPostInfo {664 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);665666 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);667668 dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))669 }670671 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]672 #[transactional]673 pub fn set_token_properties(674 origin,675 collection_id: CollectionId,676 token_id: TokenId,677 properties: Vec<Property>678 ) -> DispatchResultWithPostInfo {679 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);680681 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);682683 dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))684 }685686 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]687 #[transactional]688 pub fn delete_token_properties(689 origin,690 collection_id: CollectionId,691 token_id: TokenId,692 property_keys: Vec<PropertyKey>693 ) -> DispatchResultWithPostInfo {694 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);695696 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);697698 dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))699 }700701 #[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]702 #[transactional]703 pub fn set_property_permissions(704 origin,705 collection_id: CollectionId,706 property_permissions: Vec<PropertyKeyPermission>,707 ) -> DispatchResultWithPostInfo {708 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);709710 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);711712 dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))713 }714715 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]716 #[transactional]717 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {718 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);719 let budget = budget::Value::new(NESTING_BUDGET);720721 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))722 }723724 725726 727 728 729 730 731 732 733 734 735 736 737 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]738 #[transactional]739 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {740 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);741 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;742 target_collection.check_is_owner(&sender)?;743744 745746 target_collection.limits.transfers_enabled = Some(value);747 target_collection.save()748 }749750 751 752 753 754 755 756 757 758 759 760 761 762 763 #[weight = T::CommonWeightInfo::burn_item()]764 #[transactional]765 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {766 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);767768 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;769 if value == 1 {770 <NftTransferBasket<T>>::remove(collection_id, item_id);771 <NftApproveBasket<T>>::remove(collection_id, item_id);772 }773 774 775 776 Ok(post_info)777 }778779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 #[weight = T::CommonWeightInfo::burn_from()]796 #[transactional]797 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {798 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);799 let budget = budget::Value::new(NESTING_BUDGET);800801 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))802 }803804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 #[weight = T::CommonWeightInfo::transfer()]828 #[transactional]829 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {830 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);831 let budget = budget::Value::new(NESTING_BUDGET);832833 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))834 }835836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 #[weight = T::CommonWeightInfo::approve()]852 #[transactional]853 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {854 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);855856 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))857 }858859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 #[weight = T::CommonWeightInfo::transfer_from()]879 #[transactional]880 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {881 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);882 let budget = budget::Value::new(NESTING_BUDGET);883884 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))885 }886887 #[weight = <SelfWeightOf<T>>::set_collection_limits()]888 #[transactional]889 pub fn set_collection_limits(890 origin,891 collection_id: CollectionId,892 new_limit: CollectionLimits,893 ) -> DispatchResult {894 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);895 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;896 target_collection.check_is_owner(&sender)?;897 let old_limit = &target_collection.limits;898899 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;900901 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(902 collection_id903 ));904905 target_collection.save()906 }907908 #[weight = <SelfWeightOf<T>>::set_collection_limits()]909 #[transactional]910 pub fn set_collection_permissions(911 origin,912 collection_id: CollectionId,913 new_limit: CollectionPermissions,914 ) -> DispatchResult {915 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);916 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;917 target_collection.check_is_owner(&sender)?;918 let old_limit = &target_collection.permissions;919920 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;921922 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(923 collection_id924 ));925926 target_collection.save()927 }928 }929}