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 =336 <SelfWeightOf<T>>::destroy_collection()337 + <SelfWeightOf<T>>::burn_children_in_collection(*max_children_to_burn)338 ]339 #[transactional]340 pub fn destroy_collection(341 origin,342 collection_id: CollectionId,343 max_children_to_burn: u32,344 ) -> DispatchResult {345 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);346 let collection = <CollectionHandle<T>>::try_get(collection_id)?;347348 let budget = budget::Value::new(max_children_to_burn);349350 351352 T::CollectionDispatch::destroy(sender, collection, &budget)?;353354 <NftTransferBasket<T>>::remove_prefix(collection_id, None);355 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);356 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);357358 <NftApproveBasket<T>>::remove_prefix(collection_id, None);359 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);360 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);361362 Ok(())363 }364365 366 367 368 369 370 371 372 373 374 375 376 377 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]378 #[transactional]379 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{380381 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);382 let collection = <CollectionHandle<T>>::try_get(collection_id)?;383384 <PalletCommon<T>>::toggle_allowlist(385 &collection,386 &sender,387 &address,388 true,389 )?;390391 Self::deposit_event(Event::<T>::AllowListAddressAdded(392 collection_id,393 address394 ));395396 Ok(())397 }398399 400 401 402 403 404 405 406 407 408 409 410 411 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]412 #[transactional]413 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{414415 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);416 let collection = <CollectionHandle<T>>::try_get(collection_id)?;417418 <PalletCommon<T>>::toggle_allowlist(419 &collection,420 &sender,421 &address,422 false,423 )?;424425 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(426 collection_id,427 address428 ));429430 Ok(())431 }432433 434 435 436 437 438 439 440 441 442 443 444 #[weight = <SelfWeightOf<T>>::change_collection_owner()]445 #[transactional]446 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {447448 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);449450 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;451 target_collection.check_is_owner(&sender)?;452453 target_collection.owner = new_owner.clone();454 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(455 collection_id,456 new_owner457 ));458459 target_collection.save()460 }461462 463 464 465 466 467 468 469 470 471 472 473 474 475 #[weight = <SelfWeightOf<T>>::add_collection_admin()]476 #[transactional]477 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {478 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);479 let collection = <CollectionHandle<T>>::try_get(collection_id)?;480481 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(482 collection_id,483 new_admin_id.clone()484 ));485486 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)487 }488489 490 491 492 493 494 495 496 497 498 499 500 501 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]502 #[transactional]503 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {504 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);505 let collection = <CollectionHandle<T>>::try_get(collection_id)?;506507 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(508 collection_id,509 account_id.clone()510 ));511512 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)513 }514515 516 517 518 519 520 521 522 523 524 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]525 #[transactional]526 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {527 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);528529 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;530 target_collection.check_is_owner(&sender)?;531532 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());533534 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(535 collection_id,536 new_sponsor537 ));538539 target_collection.save()540 }541542 543 544 545 546 547 548 549 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]550 #[transactional]551 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {552 let sender = ensure_signed(origin)?;553554 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;555 ensure!(556 target_collection.sponsorship.pending_sponsor() == Some(&sender),557 Error::<T>::ConfirmUnsetSponsorFail558 );559560 target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());561562 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(563 collection_id,564 sender565 ));566567 target_collection.save()568 }569570 571 572 573 574 575 576 577 578 579 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]580 #[transactional]581 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {582 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);583584 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;585 target_collection.check_is_owner(&sender)?;586587 target_collection.sponsorship = SponsorshipState::Disabled;588589 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(590 collection_id591 ));592 target_collection.save()593 }594595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 #[weight = T::CommonWeightInfo::create_item()]614 #[transactional]615 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {616 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);617 let budget = budget::Value::new(2);618619 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))620 }621622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]641 #[transactional]642 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {643 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);644 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);645 let budget = budget::Value::new(2);646647 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))648 }649650 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]651 #[transactional]652 pub fn set_collection_properties(653 origin,654 collection_id: CollectionId,655 properties: Vec<Property>656 ) -> DispatchResultWithPostInfo {657 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);658659 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);660661 dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))662 }663664 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]665 #[transactional]666 pub fn delete_collection_properties(667 origin,668 collection_id: CollectionId,669 property_keys: Vec<PropertyKey>,670 ) -> DispatchResultWithPostInfo {671 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);672673 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);674675 dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))676 }677678 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]679 #[transactional]680 pub fn set_token_properties(681 origin,682 collection_id: CollectionId,683 token_id: TokenId,684 properties: Vec<Property>685 ) -> DispatchResultWithPostInfo {686 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);687688 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);689690 dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))691 }692693 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]694 #[transactional]695 pub fn delete_token_properties(696 origin,697 collection_id: CollectionId,698 token_id: TokenId,699 property_keys: Vec<PropertyKey>700 ) -> DispatchResultWithPostInfo {701 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);702703 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);704705 dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))706 }707708 #[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]709 #[transactional]710 pub fn set_property_permissions(711 origin,712 collection_id: CollectionId,713 property_permissions: Vec<PropertyKeyPermission>,714 ) -> DispatchResultWithPostInfo {715 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);716717 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);718719 dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))720 }721722 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]723 #[transactional]724 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {725 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);726 let budget = budget::Value::new(2);727728 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))729 }730731 732733 734 735 736 737 738 739 740 741 742 743 744 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]745 #[transactional]746 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {747 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);748 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;749 target_collection.check_is_owner(&sender)?;750751 752753 target_collection.limits.transfers_enabled = Some(value);754 target_collection.save()755 }756757 758 759 760 761 762 763 764 765 766 767 768 769 770 #[weight = T::CommonWeightInfo::burn_item()]771 #[transactional]772 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {773 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);774775 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;776 if value == 1 {777 <NftTransferBasket<T>>::remove(collection_id, item_id);778 <NftApproveBasket<T>>::remove(collection_id, item_id);779 }780 781 782 783 Ok(post_info)784 }785786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 #[weight = T::CommonWeightInfo::burn_from()]803 #[transactional]804 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {805 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);806 let budget = budget::Value::new(2);807808 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))809 }810811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 #[weight = T::CommonWeightInfo::transfer()]835 #[transactional]836 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {837 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);838 let budget = budget::Value::new(2);839840 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))841 }842843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 #[weight = T::CommonWeightInfo::approve()]859 #[transactional]860 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {861 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);862863 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))864 }865866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 #[weight = T::CommonWeightInfo::transfer_from()]886 #[transactional]887 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {888 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);889 let budget = budget::Value::new(2);890891 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))892 }893894 #[weight = <SelfWeightOf<T>>::set_collection_limits()]895 #[transactional]896 pub fn set_collection_limits(897 origin,898 collection_id: CollectionId,899 new_limit: CollectionLimits,900 ) -> DispatchResult {901 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);902 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;903 target_collection.check_is_owner(&sender)?;904 let old_limit = &target_collection.limits;905906 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;907908 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(909 collection_id910 ));911912 target_collection.save()913 }914915 #[weight = <SelfWeightOf<T>>::set_collection_limits()]916 #[transactional]917 pub fn set_collection_permissions(918 origin,919 collection_id: CollectionId,920 new_limit: CollectionPermissions,921 ) -> DispatchResult {922 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);923 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;924 target_collection.check_is_owner(&sender)?;925 let old_limit = &target_collection.permissions;926927 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;928929 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(930 collection_id931 ));932933 target_collection.save()934 }935 }936}