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 124 125 SponsorshipConfirmed(CollectionId, AccountId),126127 128 129 130 131 132 133 134 CollectionAdminRemoved(CollectionId, CrossAccountId),135136 137 138 139 140 141 142 143 AllowListAddressRemoved(CollectionId, CrossAccountId),144145 146 147 148 149 150 151 152 AllowListAddressAdded(CollectionId, CrossAccountId),153154 155 156 157 158 159 CollectionLimitSet(CollectionId),160161 CollectionPermissionSet(CollectionId),162 }163}164165type SelfWeightOf<T> = <T as Config>::WeightInfo;166167168169170171172173174175176177178179180181182183184185186187188189decl_storage! {190 trait Store for Module<T: Config> as Unique {191192 193 194 ChainVersion: u64;195 196197 198 199 200 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;201 202 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;203 204 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;205 206 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>;207 208209 210 211 #[deprecated]212 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;213 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;214215 216 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;217 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;218 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>;219 }220}221222decl_module! {223 pub struct Module<T: Config> for enum Call224 where225 origin: T::Origin226 {227 type Error = Error<T>;228229 fn deposit_event() = default;230231 fn on_initialize(_now: T::BlockNumber) -> Weight {232 0233 }234235 fn on_runtime_upgrade() -> Weight {236 let limit = None;237238 <VariableMetaDataBasket<T>>::remove_all(limit);239240 0241 }242243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 #[weight = <SelfWeightOf<T>>::create_collection()]260 #[transactional]261 #[deprecated]262 pub fn create_collection(origin,263 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,264 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,265 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,266 mode: CollectionMode) -> DispatchResult {267 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {268 name: collection_name,269 description: collection_description,270 token_prefix,271 mode,272 ..Default::default()273 };274 Self::create_collection_ex(origin, data)275 }276277 278 279 280 #[weight = <SelfWeightOf<T>>::create_collection()]281 #[transactional]282 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {283 let sender = ensure_signed(origin)?;284285 286287 T::CollectionDispatch::create(sender, data)?;288289 Ok(())290 }291292 293 294 295 296 297 298 299 300 301 #[weight = <SelfWeightOf<T>>::destroy_collection()]302 #[transactional]303 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {304 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);305 let collection = <CollectionHandle<T>>::try_get(collection_id)?;306307 308309 T::CollectionDispatch::destroy(sender, collection)?;310311 <NftTransferBasket<T>>::remove_prefix(collection_id, None);312 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);313 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);314315 <NftApproveBasket<T>>::remove_prefix(collection_id, None);316 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);317 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);318319 Ok(())320 }321322 323 324 325 326 327 328 329 330 331 332 333 334 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]335 #[transactional]336 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{337338 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);339 let collection = <CollectionHandle<T>>::try_get(collection_id)?;340341 <PalletCommon<T>>::toggle_allowlist(342 &collection,343 &sender,344 &address,345 true,346 )?;347348 Self::deposit_event(Event::<T>::AllowListAddressAdded(349 collection_id,350 address351 ));352353 Ok(())354 }355356 357 358 359 360 361 362 363 364 365 366 367 368 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]369 #[transactional]370 pub fn remove_from_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 false,380 )?;381382 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(383 collection_id,384 address385 ));386387 Ok(())388 }389390 391 392 393 394 395 396 397 398 399 400 401 #[weight = <SelfWeightOf<T>>::change_collection_owner()]402 #[transactional]403 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {404405 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);406407 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;408 target_collection.check_is_owner(&sender)?;409410 target_collection.owner = new_owner.clone();411 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(412 collection_id,413 new_owner414 ));415416 target_collection.save()417 }418419 420 421 422 423 424 425 426 427 428 429 430 431 432 #[weight = <SelfWeightOf<T>>::add_collection_admin()]433 #[transactional]434 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {435 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);436 let collection = <CollectionHandle<T>>::try_get(collection_id)?;437438 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(439 collection_id,440 new_admin_id.clone()441 ));442443 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)444 }445446 447 448 449 450 451 452 453 454 455 456 457 458 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]459 #[transactional]460 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {461 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);462 let collection = <CollectionHandle<T>>::try_get(collection_id)?;463464 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(465 collection_id,466 account_id.clone()467 ));468469 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)470 }471472 473 474 475 476 477 478 479 480 481 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]482 #[transactional]483 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {484 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);485486 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;487 target_collection.check_is_owner(&sender)?;488489 target_collection.set_sponsor(new_sponsor.clone());490491 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(492 collection_id,493 new_sponsor494 ));495496 target_collection.save()497 }498499 500 501 502 503 504 505 506 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]507 #[transactional]508 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {509 let sender = ensure_signed(origin)?;510511 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;512 ensure!(513 target_collection.confirm_sponsorship(&sender),514 Error::<T>::ConfirmUnsetSponsorFail515 );516517 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(518 collection_id,519 sender520 ));521522 target_collection.save()523 }524525 526 527 528 529 530 531 532 533 534 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]535 #[transactional]536 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {537 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);538539 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;540 target_collection.check_is_owner(&sender)?;541542 target_collection.sponsorship = SponsorshipState::Disabled;543544 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(545 collection_id546 ));547 target_collection.save()548 }549550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 #[weight = T::CommonWeightInfo::create_item()]569 #[transactional]570 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {571 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);572 let budget = budget::Value::new(2);573574 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))575 }576577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]596 #[transactional]597 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {598 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);599 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);600 let budget = budget::Value::new(2);601602 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))603 }604605 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]606 #[transactional]607 pub fn set_collection_properties(608 origin,609 collection_id: CollectionId,610 properties: Vec<Property>611 ) -> DispatchResultWithPostInfo {612 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);613614 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);615616 dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))617 }618619 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]620 #[transactional]621 pub fn delete_collection_properties(622 origin,623 collection_id: CollectionId,624 property_keys: Vec<PropertyKey>,625 ) -> DispatchResultWithPostInfo {626 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);627628 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);629630 dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))631 }632633 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]634 #[transactional]635 pub fn set_token_properties(636 origin,637 collection_id: CollectionId,638 token_id: TokenId,639 properties: Vec<Property>640 ) -> DispatchResultWithPostInfo {641 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);642643 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);644645 dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))646 }647648 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]649 #[transactional]650 pub fn delete_token_properties(651 origin,652 collection_id: CollectionId,653 token_id: TokenId,654 property_keys: Vec<PropertyKey>655 ) -> DispatchResultWithPostInfo {656 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);657658 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);659660 dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))661 }662663 #[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]664 #[transactional]665 pub fn set_property_permissions(666 origin,667 collection_id: CollectionId,668 property_permissions: Vec<PropertyKeyPermission>,669 ) -> DispatchResultWithPostInfo {670 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);671672 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);673674 dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))675 }676677 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]678 #[transactional]679 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {680 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);681 let budget = budget::Value::new(2);682683 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))684 }685686 687688 689 690 691 692 693 694 695 696 697 698 699 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]700 #[transactional]701 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {702 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);703 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;704 target_collection.check_is_owner(&sender)?;705706 707708 target_collection.limits.transfers_enabled = Some(value);709 target_collection.save()710 }711712 713 714 715 716 717 718 719 720 721 722 723 724 725 #[weight = T::CommonWeightInfo::burn_item()]726 #[transactional]727 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {728 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);729730 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;731 if value == 1 {732 <NftTransferBasket<T>>::remove(collection_id, item_id);733 <NftApproveBasket<T>>::remove(collection_id, item_id);734 }735 736 737 738 Ok(post_info)739 }740741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 #[weight = T::CommonWeightInfo::burn_from()]758 #[transactional]759 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {760 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);761 let budget = budget::Value::new(2);762763 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))764 }765766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 #[weight = T::CommonWeightInfo::transfer()]790 #[transactional]791 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {792 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);793 let budget = budget::Value::new(2);794795 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))796 }797798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 #[weight = T::CommonWeightInfo::approve()]814 #[transactional]815 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {816 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);817818 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))819 }820821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 #[weight = T::CommonWeightInfo::transfer_from()]841 #[transactional]842 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {843 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);844 let budget = budget::Value::new(2);845846 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))847 }848849 #[weight = <SelfWeightOf<T>>::set_collection_limits()]850 #[transactional]851 pub fn set_collection_limits(852 origin,853 collection_id: CollectionId,854 new_limit: CollectionLimits,855 ) -> DispatchResult {856 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);857 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;858 target_collection.check_is_owner(&sender)?;859 let old_limit = &target_collection.limits;860861 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;862863 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(864 collection_id865 ));866867 target_collection.save()868 }869870 #[weight = <SelfWeightOf<T>>::set_collection_limits()]871 #[transactional]872 pub fn set_collection_permissions(873 origin,874 collection_id: CollectionId,875 new_limit: CollectionPermissions,876 ) -> DispatchResult {877 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);878 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;879 target_collection.check_is_owner(&sender)?;880 let old_limit = &target_collection.permissions;881882 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;883884 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(885 collection_id886 ));887888 target_collection.save()889 }890 }891}