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)]2425extern crate alloc;2627use frame_support::{28 decl_module, decl_storage, decl_error, decl_event,29 dispatch::DispatchResult,30 ensure,31 weights::{Weight},32 transactional,33 pallet_prelude::{DispatchResultWithPostInfo, ConstU32},34 BoundedVec,35};36use scale_info::TypeInfo;37use frame_system::{self as system, ensure_signed};38use sp_runtime::{sp_std::prelude::Vec};39use up_data_structs::{40 MAX_COLLECTION_NAME_LENGTH,41 MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData,42 CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId, SponsorshipState,43 CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,44 PropertyKeyPermission,45};46use pallet_evm::account::CrossAccountId;47use pallet_common::{48 CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_call,49 dispatch::CollectionDispatch,50};51pub mod eth;5253#[cfg(feature = "runtime-benchmarks")]54mod benchmarking;55pub mod weights;56use weights::WeightInfo;5758decl_error! {59 60 pub enum Error for Module<T: Config> {61 62 CollectionDecimalPointLimitExceeded,63 64 ConfirmUnsetSponsorFail,65 66 EmptyArgument,67 }68}6970pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {71 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7273 74 type WeightInfo: WeightInfo;75 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;76}7778decl_event! {79 pub enum Event<T>80 where81 <T as frame_system::Config>::AccountId,82 <T as pallet_evm::account::Config>::CrossAccountId,83 {84 85 86 87 88 89 CollectionSponsorRemoved(CollectionId),9091 92 93 94 95 96 97 98 CollectionAdminAdded(CollectionId, CrossAccountId),99100 101 102 103 104 105 106 107 CollectionOwnedChanged(CollectionId, AccountId),108109 110 111 112 113 114 115 116 CollectionSponsorSet(CollectionId, AccountId),117118 119 120 121 122 123 ConstOnChainSchemaSet(CollectionId),124125 126 127 128 129 130 131 132 SponsorshipConfirmed(CollectionId, AccountId),133134 135 136 137 138 139 140 141 CollectionAdminRemoved(CollectionId, CrossAccountId),142143 144 145 146 147 148 149 150 AllowListAddressRemoved(CollectionId, CrossAccountId),151152 153 154 155 156 157 158 159 AllowListAddressAdded(CollectionId, CrossAccountId),160161 162 163 164 165 166 CollectionLimitSet(CollectionId),167168 CollectionPermissionSet(CollectionId),169170 171 172 173 174 175 MintPermissionSet(CollectionId),176177 178 179 180 181 182 OffchainSchemaSet(CollectionId),183184 185 186 187 188 189 190 191 PublicAccessModeSet(CollectionId, AccessMode),192193 194 195 196 197 198 SchemaVersionSet(CollectionId),199 }200}201202type SelfWeightOf<T> = <T as Config>::WeightInfo;203204205206207208209210211212213214215216217218219220221222223224225226decl_storage! {227 trait Store for Module<T: Config> as Unique {228229 230 231 ChainVersion: u64;232 233234 235 236 237 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;238 239 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;240 241 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;242 243 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>;244 245246 247 248 #[deprecated]249 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;250 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;251252 253 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;254 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;255 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>;256 }257}258259decl_module! {260 pub struct Module<T: Config> for enum Call261 where262 origin: T::Origin263 {264 type Error = Error<T>;265266 fn deposit_event() = default;267268 fn on_initialize(_now: T::BlockNumber) -> Weight {269 0270 }271272 fn on_runtime_upgrade() -> Weight {273 let limit = None;274275 <VariableMetaDataBasket<T>>::remove_all(limit);276277 0278 }279280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 #[weight = <SelfWeightOf<T>>::create_collection()]297 #[transactional]298 #[deprecated]299 pub fn create_collection(origin,300 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,301 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,302 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,303 mode: CollectionMode) -> DispatchResult {304 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {305 name: collection_name,306 description: collection_description,307 token_prefix,308 mode,309 ..Default::default()310 };311 Self::create_collection_ex(origin, data)312 }313314 315 316 317 #[weight = <SelfWeightOf<T>>::create_collection()]318 #[transactional]319 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {320 let sender = ensure_signed(origin)?;321322 323324 T::CollectionDispatch::create(sender, data)?;325326 Ok(())327 }328329 330 331 332 333 334 335 336 337 338 #[weight = <SelfWeightOf<T>>::destroy_collection()]339 #[transactional]340 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {341 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);342 let collection = <CollectionHandle<T>>::try_get(collection_id)?;343344 345346 T::CollectionDispatch::destroy(sender, collection)?;347348 <NftTransferBasket<T>>::remove_prefix(collection_id, None);349 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);350 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);351352 <NftApproveBasket<T>>::remove_prefix(collection_id, None);353 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);354 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);355356 Ok(())357 }358359 360 361 362 363 364 365 366 367 368 369 370 371 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]372 #[transactional]373 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{374375 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);376 let collection = <CollectionHandle<T>>::try_get(collection_id)?;377378 <PalletCommon<T>>::toggle_allowlist(379 &collection,380 &sender,381 &address,382 true,383 )?;384385 Self::deposit_event(Event::<T>::AllowListAddressAdded(386 collection_id,387 address388 ));389390 Ok(())391 }392393 394 395 396 397 398 399 400 401 402 403 404 405 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]406 #[transactional]407 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{408409 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);410 let collection = <CollectionHandle<T>>::try_get(collection_id)?;411412 <PalletCommon<T>>::toggle_allowlist(413 &collection,414 &sender,415 &address,416 false,417 )?;418419 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(420 collection_id,421 address422 ));423424 Ok(())425 }426427 428 429 430 431 432 433 434 435 436 437 438 #[weight = <SelfWeightOf<T>>::change_collection_owner()]439 #[transactional]440 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {441442 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);443444 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;445 target_collection.check_is_owner(&sender)?;446447 target_collection.owner = new_owner.clone();448 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(449 collection_id,450 new_owner451 ));452453 target_collection.save()454 }455456 457 458 459 460 461 462 463 464 465 466 467 468 469 #[weight = <SelfWeightOf<T>>::add_collection_admin()]470 #[transactional]471 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {472 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);473 let collection = <CollectionHandle<T>>::try_get(collection_id)?;474475 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(476 collection_id,477 new_admin_id.clone()478 ));479480 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)481 }482483 484 485 486 487 488 489 490 491 492 493 494 495 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]496 #[transactional]497 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {498 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);499 let collection = <CollectionHandle<T>>::try_get(collection_id)?;500501 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(502 collection_id,503 account_id.clone()504 ));505506 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)507 }508509 510 511 512 513 514 515 516 517 518 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]519 #[transactional]520 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {521 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);522523 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;524 target_collection.check_is_owner(&sender)?;525526 target_collection.set_sponsor(new_sponsor.clone());527528 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(529 collection_id,530 new_sponsor531 ));532533 target_collection.save()534 }535536 537 538 539 540 541 542 543 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]544 #[transactional]545 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {546 let sender = ensure_signed(origin)?;547548 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;549 ensure!(550 target_collection.confirm_sponsorship(&sender),551 Error::<T>::ConfirmUnsetSponsorFail552 );553554 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(555 collection_id,556 sender557 ));558559 target_collection.save()560 }561562 563 564 565 566 567 568 569 570 571 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]572 #[transactional]573 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {574 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);575576 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;577 target_collection.check_is_owner(&sender)?;578579 target_collection.sponsorship = SponsorshipState::Disabled;580581 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(582 collection_id583 ));584 target_collection.save()585 }586587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 #[weight = T::CommonWeightInfo::create_item()]606 #[transactional]607 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {608 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);609 let budget = budget::Value::new(2);610611 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))612 }613614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]633 #[transactional]634 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {635 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);636 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);637 let budget = budget::Value::new(2);638639 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))640 }641642 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]643 #[transactional]644 pub fn set_collection_properties(645 origin,646 collection_id: CollectionId,647 properties: Vec<Property>648 ) -> DispatchResultWithPostInfo {649 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);650651 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);652653 dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))654 }655656 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]657 #[transactional]658 pub fn delete_collection_properties(659 origin,660 collection_id: CollectionId,661 property_keys: Vec<PropertyKey>,662 ) -> DispatchResultWithPostInfo {663 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);664665 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);666667 dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))668 }669670 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]671 #[transactional]672 pub fn set_token_properties(673 origin,674 collection_id: CollectionId,675 token_id: TokenId,676 properties: Vec<Property>677 ) -> DispatchResultWithPostInfo {678 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);679680 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);681682 dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))683 }684685 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]686 #[transactional]687 pub fn delete_token_properties(688 origin,689 collection_id: CollectionId,690 token_id: TokenId,691 property_keys: Vec<PropertyKey>692 ) -> DispatchResultWithPostInfo {693 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);694695 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);696697 dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))698 }699700 #[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]701 #[transactional]702 pub fn set_property_permissions(703 origin,704 collection_id: CollectionId,705 property_permissions: Vec<PropertyKeyPermission>,706 ) -> DispatchResultWithPostInfo {707 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);708709 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);710711 dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))712 }713714 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]715 #[transactional]716 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {717 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);718 let budget = budget::Value::new(2);719720 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))721 }722723 724725 726 727 728 729 730 731 732 733 734 735 736 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]737 #[transactional]738 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {739 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);740 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;741 target_collection.check_is_owner(&sender)?;742743 744745 target_collection.limits.transfers_enabled = Some(value);746 target_collection.save()747 }748749 750 751 752 753 754 755 756 757 758 759 760 761 762 #[weight = T::CommonWeightInfo::burn_item()]763 #[transactional]764 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {765 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);766767 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;768 if value == 1 {769 <NftTransferBasket<T>>::remove(collection_id, item_id);770 <NftApproveBasket<T>>::remove(collection_id, item_id);771 }772 773 774 775 Ok(post_info)776 }777778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 #[weight = T::CommonWeightInfo::burn_from()]795 #[transactional]796 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {797 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);798 let budget = budget::Value::new(2);799800 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))801 }802803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 #[weight = T::CommonWeightInfo::transfer()]827 #[transactional]828 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {829 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);830 let budget = budget::Value::new(2);831832 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))833 }834835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 #[weight = T::CommonWeightInfo::approve()]851 #[transactional]852 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {853 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);854855 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))856 }857858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 #[weight = T::CommonWeightInfo::transfer_from()]878 #[transactional]879 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {880 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);881 let budget = budget::Value::new(2);882883 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))884 }885886 #[weight = <SelfWeightOf<T>>::set_collection_limits()]887 #[transactional]888 pub fn set_collection_limits(889 origin,890 collection_id: CollectionId,891 new_limit: CollectionLimits,892 ) -> DispatchResult {893 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);894 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;895 target_collection.check_is_owner(&sender)?;896 let old_limit = &target_collection.limits;897898 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;899900 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(901 collection_id902 ));903904 target_collection.save()905 }906907 #[weight = <SelfWeightOf<T>>::set_collection_limits()]908 #[transactional]909 pub fn set_collection_permissions(910 origin,911 collection_id: CollectionId,912 new_limit: CollectionPermissions,913 ) -> DispatchResult {914 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);915 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;916 target_collection.check_is_owner(&sender)?;917 let old_limit = &target_collection.permissions;918919 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;920921 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(922 collection_id923 ));924925 target_collection.save()926 }927 }928}