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.set_sponsor(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.confirm_sponsorship(&sender),548 Error::<T>::ConfirmUnsetSponsorFail549 );550551 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(552 collection_id,553 sender554 ));555556 target_collection.save()557 }558559 560 561 562 563 564 565 566 567 568 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]569 #[transactional]570 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {571 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);572573 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;574 target_collection.check_is_owner(&sender)?;575576 target_collection.sponsorship = SponsorshipState::Disabled;577578 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(579 collection_id580 ));581 target_collection.save()582 }583584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 #[weight = T::CommonWeightInfo::create_item()]603 #[transactional]604 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {605 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);606 let budget = budget::Value::new(2);607608 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))609 }610611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]630 #[transactional]631 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {632 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);633 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);634 let budget = budget::Value::new(2);635636 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))637 }638639 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]640 #[transactional]641 pub fn set_collection_properties(642 origin,643 collection_id: CollectionId,644 properties: Vec<Property>645 ) -> DispatchResultWithPostInfo {646 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);647648 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);649650 dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))651 }652653 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]654 #[transactional]655 pub fn delete_collection_properties(656 origin,657 collection_id: CollectionId,658 property_keys: Vec<PropertyKey>,659 ) -> DispatchResultWithPostInfo {660 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);661662 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);663664 dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))665 }666667 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]668 #[transactional]669 pub fn set_token_properties(670 origin,671 collection_id: CollectionId,672 token_id: TokenId,673 properties: Vec<Property>674 ) -> DispatchResultWithPostInfo {675 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);676677 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);678679 dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))680 }681682 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]683 #[transactional]684 pub fn delete_token_properties(685 origin,686 collection_id: CollectionId,687 token_id: TokenId,688 property_keys: Vec<PropertyKey>689 ) -> DispatchResultWithPostInfo {690 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);691692 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);693694 dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))695 }696697 #[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]698 #[transactional]699 pub fn set_property_permissions(700 origin,701 collection_id: CollectionId,702 property_permissions: Vec<PropertyKeyPermission>,703 ) -> DispatchResultWithPostInfo {704 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);705706 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);707708 dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))709 }710711 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]712 #[transactional]713 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {714 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);715 let budget = budget::Value::new(2);716717 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))718 }719720 721722 723 724 725 726 727 728 729 730 731 732 733 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]734 #[transactional]735 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {736 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);737 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;738 target_collection.check_is_owner(&sender)?;739740 741742 target_collection.limits.transfers_enabled = Some(value);743 target_collection.save()744 }745746 747 748 749 750 751 752 753 754 755 756 757 758 759 #[weight = T::CommonWeightInfo::burn_item()]760 #[transactional]761 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {762 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);763764 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;765 if value == 1 {766 <NftTransferBasket<T>>::remove(collection_id, item_id);767 <NftApproveBasket<T>>::remove(collection_id, item_id);768 }769 770 771 772 Ok(post_info)773 }774775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 #[weight = T::CommonWeightInfo::burn_from()]792 #[transactional]793 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {794 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);795 let budget = budget::Value::new(2);796797 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))798 }799800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 #[weight = T::CommonWeightInfo::transfer()]824 #[transactional]825 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {826 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);827 let budget = budget::Value::new(2);828829 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))830 }831832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 #[weight = T::CommonWeightInfo::approve()]848 #[transactional]849 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {850 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);851852 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))853 }854855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 #[weight = T::CommonWeightInfo::transfer_from()]875 #[transactional]876 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {877 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);878 let budget = budget::Value::new(2);879880 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))881 }882883 #[weight = <SelfWeightOf<T>>::set_collection_limits()]884 #[transactional]885 pub fn set_collection_limits(886 origin,887 collection_id: CollectionId,888 new_limit: CollectionLimits,889 ) -> DispatchResult {890 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);891 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;892 target_collection.check_is_owner(&sender)?;893 let old_limit = &target_collection.limits;894895 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;896897 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(898 collection_id899 ));900901 target_collection.save()902 }903904 #[weight = <SelfWeightOf<T>>::set_collection_limits()]905 #[transactional]906 pub fn set_collection_permissions(907 origin,908 collection_id: CollectionId,909 new_limit: CollectionPermissions,910 ) -> DispatchResult {911 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);912 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;913 target_collection.check_is_owner(&sender)?;914 let old_limit = &target_collection.permissions;915916 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;917918 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(919 collection_id920 ));921922 target_collection.save()923 }924 }925}