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;5455decl_error! {56 57 pub enum Error for Module<T: Config> {58 59 CollectionDecimalPointLimitExceeded,60 61 ConfirmUnsetSponsorFail,62 63 EmptyArgument,64 }65}6667pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {68 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;6970 71 type WeightInfo: WeightInfo;72 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;73}7475decl_event! {76 pub enum Event<T>77 where78 <T as frame_system::Config>::AccountId,79 <T as pallet_evm::account::Config>::CrossAccountId,80 {81 82 83 84 85 86 CollectionSponsorRemoved(CollectionId),8788 89 90 91 92 93 94 95 CollectionAdminAdded(CollectionId, CrossAccountId),9697 98 99 100 101 102 103 104 CollectionOwnedChanged(CollectionId, AccountId),105106 107 108 109 110 111 112 113 CollectionSponsorSet(CollectionId, AccountId),114115 116 117 118 119 120 ConstOnChainSchemaSet(CollectionId),121122 123 124 125 126 127 128 129 SponsorshipConfirmed(CollectionId, AccountId),130131 132 133 134 135 136 137 138 CollectionAdminRemoved(CollectionId, CrossAccountId),139140 141 142 143 144 145 146 147 AllowListAddressRemoved(CollectionId, CrossAccountId),148149 150 151 152 153 154 155 156 AllowListAddressAdded(CollectionId, CrossAccountId),157158 159 160 161 162 163 CollectionLimitSet(CollectionId),164165 CollectionPermissionSet(CollectionId),166167 168 169 170 171 172 MintPermissionSet(CollectionId),173174 175 176 177 178 179 OffchainSchemaSet(CollectionId),180181 182 183 184 185 186 187 188 PublicAccessModeSet(CollectionId, AccessMode),189190 191 192 193 194 195 SchemaVersionSet(CollectionId),196 }197}198199type SelfWeightOf<T> = <T as Config>::WeightInfo;200201202203204205206207208209210211212213214215216217218219220221222223decl_storage! {224 trait Store for Module<T: Config> as Unique {225226 227 228 ChainVersion: u64;229 230231 232 233 234 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;235 236 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;237 238 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;239 240 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>;241 242243 244 245 #[deprecated]246 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;247 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;248249 250 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;251 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;252 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>;253 }254}255256decl_module! {257 pub struct Module<T: Config> for enum Call258 where259 origin: T::Origin260 {261 type Error = Error<T>;262263 fn deposit_event() = default;264265 fn on_initialize(_now: T::BlockNumber) -> Weight {266 0267 }268269 fn on_runtime_upgrade() -> Weight {270 let limit = None;271272 <VariableMetaDataBasket<T>>::remove_all(limit);273274 0275 }276277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 #[weight = <SelfWeightOf<T>>::create_collection()]294 #[transactional]295 #[deprecated]296 pub fn create_collection(origin,297 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,298 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,299 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,300 mode: CollectionMode) -> DispatchResult {301 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {302 name: collection_name,303 description: collection_description,304 token_prefix,305 mode,306 ..Default::default()307 };308 Self::create_collection_ex(origin, data)309 }310311 312 313 314 #[weight = <SelfWeightOf<T>>::create_collection()]315 #[transactional]316 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {317 let sender = ensure_signed(origin)?;318319 320321 T::CollectionDispatch::create(sender, data)?;322323 Ok(())324 }325326 327 328 329 330 331 332 333 334 335 #[weight = <SelfWeightOf<T>>::destroy_collection()]336 #[transactional]337 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {338 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);339 let collection = <CollectionHandle<T>>::try_get(collection_id)?;340341 342343 T::CollectionDispatch::destroy(sender, collection)?;344345 <NftTransferBasket<T>>::remove_prefix(collection_id, None);346 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);347 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);348349 <NftApproveBasket<T>>::remove_prefix(collection_id, None);350 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);351 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);352353 Ok(())354 }355356 357 358 359 360 361 362 363 364 365 366 367 368 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]369 #[transactional]370 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{371372 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);373 let collection = <CollectionHandle<T>>::try_get(collection_id)?;374375 <PalletCommon<T>>::toggle_allowlist(376 &collection,377 &sender,378 &address,379 true,380 )?;381382 Self::deposit_event(Event::<T>::AllowListAddressAdded(383 collection_id,384 address385 ));386387 Ok(())388 }389390 391 392 393 394 395 396 397 398 399 400 401 402 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]403 #[transactional]404 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{405406 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);407 let collection = <CollectionHandle<T>>::try_get(collection_id)?;408409 <PalletCommon<T>>::toggle_allowlist(410 &collection,411 &sender,412 &address,413 false,414 )?;415416 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(417 collection_id,418 address419 ));420421 Ok(())422 }423424 425 426 427 428 429 430 431 432 433 434 435 #[weight = <SelfWeightOf<T>>::change_collection_owner()]436 #[transactional]437 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {438439 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);440441 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;442 target_collection.check_is_owner(&sender)?;443444 target_collection.owner = new_owner.clone();445 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(446 collection_id,447 new_owner448 ));449450 target_collection.save()451 }452453 454 455 456 457 458 459 460 461 462 463 464 465 466 #[weight = <SelfWeightOf<T>>::add_collection_admin()]467 #[transactional]468 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {469 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);470 let collection = <CollectionHandle<T>>::try_get(collection_id)?;471472 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(473 collection_id,474 new_admin_id.clone()475 ));476477 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)478 }479480 481 482 483 484 485 486 487 488 489 490 491 492 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]493 #[transactional]494 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {495 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);496 let collection = <CollectionHandle<T>>::try_get(collection_id)?;497498 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(499 collection_id,500 account_id.clone()501 ));502503 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)504 }505506 507 508 509 510 511 512 513 514 515 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]516 #[transactional]517 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {518 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);519520 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;521 target_collection.check_is_owner(&sender)?;522523 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());524525 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(526 collection_id,527 new_sponsor528 ));529530 target_collection.save()531 }532533 534 535 536 537 538 539 540 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]541 #[transactional]542 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {543 let sender = ensure_signed(origin)?;544545 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;546 ensure!(547 target_collection.sponsorship.pending_sponsor() == Some(&sender),548 Error::<T>::ConfirmUnsetSponsorFail549 );550551 target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());552553 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(554 collection_id,555 sender556 ));557558 target_collection.save()559 }560561 562 563 564 565 566 567 568 569 570 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]571 #[transactional]572 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {573 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);574575 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;576 target_collection.check_is_owner(&sender)?;577578 target_collection.sponsorship = SponsorshipState::Disabled;579580 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(581 collection_id582 ));583 target_collection.save()584 }585586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 #[weight = T::CommonWeightInfo::create_item()]605 #[transactional]606 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {607 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);608 let budget = budget::Value::new(2);609610 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))611 }612613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]632 #[transactional]633 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {634 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);635 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);636 let budget = budget::Value::new(2);637638 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))639 }640641 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]642 #[transactional]643 pub fn set_collection_properties(644 origin,645 collection_id: CollectionId,646 properties: Vec<Property>647 ) -> DispatchResultWithPostInfo {648 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);649650 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);651652 dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))653 }654655 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]656 #[transactional]657 pub fn delete_collection_properties(658 origin,659 collection_id: CollectionId,660 property_keys: Vec<PropertyKey>,661 ) -> DispatchResultWithPostInfo {662 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);663664 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);665666 dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))667 }668669 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]670 #[transactional]671 pub fn set_token_properties(672 origin,673 collection_id: CollectionId,674 token_id: TokenId,675 properties: Vec<Property>676 ) -> DispatchResultWithPostInfo {677 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);678679 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);680681 dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))682 }683684 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]685 #[transactional]686 pub fn delete_token_properties(687 origin,688 collection_id: CollectionId,689 token_id: TokenId,690 property_keys: Vec<PropertyKey>691 ) -> DispatchResultWithPostInfo {692 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);693694 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);695696 dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))697 }698699 #[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]700 #[transactional]701 pub fn set_property_permissions(702 origin,703 collection_id: CollectionId,704 property_permissions: Vec<PropertyKeyPermission>,705 ) -> DispatchResultWithPostInfo {706 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);707708 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);709710 dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))711 }712713 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]714 #[transactional]715 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {716 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);717 let budget = budget::Value::new(2);718719 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))720 }721722 723724 725 726 727 728 729 730 731 732 733 734 735 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]736 #[transactional]737 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {738 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);739 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;740 target_collection.check_is_owner(&sender)?;741742 743744 target_collection.limits.transfers_enabled = Some(value);745 target_collection.save()746 }747748 749 750 751 752 753 754 755 756 757 758 759 760 761 #[weight = T::CommonWeightInfo::burn_item()]762 #[transactional]763 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {764 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);765766 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;767 if value == 1 {768 <NftTransferBasket<T>>::remove(collection_id, item_id);769 <NftApproveBasket<T>>::remove(collection_id, item_id);770 }771 772 773 774 Ok(post_info)775 }776777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 #[weight = T::CommonWeightInfo::burn_from()]794 #[transactional]795 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {796 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);797 let budget = budget::Value::new(2);798799 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))800 }801802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 #[weight = T::CommonWeightInfo::transfer()]826 #[transactional]827 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {828 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);829 let budget = budget::Value::new(2);830831 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))832 }833834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 #[weight = T::CommonWeightInfo::approve()]850 #[transactional]851 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {852 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);853854 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))855 }856857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 #[weight = T::CommonWeightInfo::transfer_from()]877 #[transactional]878 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {879 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);880 let budget = budget::Value::new(2);881882 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))883 }884885 #[weight = <SelfWeightOf<T>>::set_collection_limits()]886 #[transactional]887 pub fn set_collection_limits(888 origin,889 collection_id: CollectionId,890 new_limit: CollectionLimits,891 ) -> DispatchResult {892 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);893 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;894 target_collection.check_is_owner(&sender)?;895 let old_limit = &target_collection.limits;896897 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;898899 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(900 collection_id901 ));902903 target_collection.save()904 }905906 #[weight = <SelfWeightOf<T>>::set_collection_limits()]907 #[transactional]908 pub fn set_collection_permissions(909 origin,910 collection_id: CollectionId,911 new_limit: CollectionPermissions,912 ) -> DispatchResult {913 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);914 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;915 target_collection.check_is_owner(&sender)?;916 let old_limit = &target_collection.permissions;917918 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;919920 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(921 collection_id922 ));923924 target_collection.save()925 }926 }927}