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, fail,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, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,41 CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,42 SponsorshipState, CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,43 PropertyKeyPermission,44};45use pallet_evm::account::CrossAccountId;46use pallet_common::{47 CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,48 dispatch::CollectionDispatch,49};50use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};51pub mod eth;5253#[cfg(feature = "runtime-benchmarks")]54mod benchmarking;55pub mod weights;56use weights::WeightInfo;5758const NESTING_BUDGET: u32 = 5;5960decl_error! {61 62 pub enum Error for Module<T: Config> {63 64 CollectionDecimalPointLimitExceeded,65 66 ConfirmUnsetSponsorFail,67 68 EmptyArgument,69 70 RepartitionCalledOnNonRefungibleCollection,71 }72}7374pub trait Config:75 system::Config + pallet_common::Config + pallet_refungible::Config + Sized + TypeInfo76{77 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7879 80 type WeightInfo: WeightInfo;81 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;82}8384decl_event! {85 pub enum Event<T>86 where87 <T as frame_system::Config>::AccountId,88 <T as pallet_evm::account::Config>::CrossAccountId,89 {90 91 92 93 94 95 CollectionSponsorRemoved(CollectionId),9697 98 99 100 101 102 103 104 CollectionAdminAdded(CollectionId, CrossAccountId),105106 107 108 109 110 111 112 113 CollectionOwnedChanged(CollectionId, AccountId),114115 116 117 118 119 120 121 122 CollectionSponsorSet(CollectionId, AccountId),123124 125 126 127 128 129 130 131 SponsorshipConfirmed(CollectionId, AccountId),132133 134 135 136 137 138 139 140 CollectionAdminRemoved(CollectionId, CrossAccountId),141142 143 144 145 146 147 148 149 AllowListAddressRemoved(CollectionId, CrossAccountId),150151 152 153 154 155 156 157 158 AllowListAddressAdded(CollectionId, CrossAccountId),159160 161 162 163 164 165 CollectionLimitSet(CollectionId),166167 CollectionPermissionSet(CollectionId),168 }169}170171type SelfWeightOf<T> = <T as Config>::WeightInfo;172173174175176177178179180181182183184185186187188189190191192193194195decl_storage! {196 trait Store for Module<T: Config> as Unique {197198 199 200 ChainVersion: u64;201 202203 204 205 206 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;207 208 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;209 210 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;211 212 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>;213 214215 216 217 #[deprecated]218 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;219 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;220221 222 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;223 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;224 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>;225 }226}227228decl_module! {229 pub struct Module<T: Config> for enum Call230 where231 origin: T::Origin232 {233 type Error = Error<T>;234235 fn deposit_event() = default;236237 fn on_initialize(_now: T::BlockNumber) -> Weight {238 0239 }240241 fn on_runtime_upgrade() -> Weight {242 let limit = None;243244 <VariableMetaDataBasket<T>>::remove_all(limit);245246 0247 }248249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 #[weight = <SelfWeightOf<T>>::create_collection()]266 #[transactional]267 #[deprecated]268 pub fn create_collection(origin,269 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,270 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,271 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,272 mode: CollectionMode) -> DispatchResult {273 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {274 name: collection_name,275 description: collection_description,276 token_prefix,277 mode,278 ..Default::default()279 };280 Self::create_collection_ex(origin, data)281 }282283 284 285 286 #[weight = <SelfWeightOf<T>>::create_collection()]287 #[transactional]288 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {289 let sender = ensure_signed(origin)?;290291 292293 T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;294295 Ok(())296 }297298 299 300 301 302 303 304 305 306 307 #[weight = <SelfWeightOf<T>>::destroy_collection()]308 #[transactional]309 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {310 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);311 let collection = <CollectionHandle<T>>::try_get(collection_id)?;312 collection.check_is_internal()?;313314 315316 T::CollectionDispatch::destroy(sender, collection)?;317318 <NftTransferBasket<T>>::remove_prefix(collection_id, None);319 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);320 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);321322 <NftApproveBasket<T>>::remove_prefix(collection_id, None);323 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);324 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);325326 Ok(())327 }328329 330 331 332 333 334 335 336 337 338 339 340 341 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]342 #[transactional]343 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{344345 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);346 let collection = <CollectionHandle<T>>::try_get(collection_id)?;347 collection.check_is_internal()?;348349 <PalletCommon<T>>::toggle_allowlist(350 &collection,351 &sender,352 &address,353 true,354 )?;355356 Self::deposit_event(Event::<T>::AllowListAddressAdded(357 collection_id,358 address359 ));360361 Ok(())362 }363364 365 366 367 368 369 370 371 372 373 374 375 376 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]377 #[transactional]378 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{379380 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);381 let collection = <CollectionHandle<T>>::try_get(collection_id)?;382 collection.check_is_internal()?;383384 <PalletCommon<T>>::toggle_allowlist(385 &collection,386 &sender,387 &address,388 false,389 )?;390391 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(392 collection_id,393 address394 ));395396 Ok(())397 }398399 400 401 402 403 404 405 406 407 408 409 410 #[weight = <SelfWeightOf<T>>::change_collection_owner()]411 #[transactional]412 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {413414 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);415416 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;417 target_collection.check_is_internal()?;418 target_collection.check_is_owner(&sender)?;419420 target_collection.owner = new_owner.clone();421 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(422 collection_id,423 new_owner424 ));425426 target_collection.save()427 }428429 430 431 432 433 434 435 436 437 438 439 440 441 442 #[weight = <SelfWeightOf<T>>::add_collection_admin()]443 #[transactional]444 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {445 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);446 let collection = <CollectionHandle<T>>::try_get(collection_id)?;447 collection.check_is_internal()?;448449 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(450 collection_id,451 new_admin_id.clone()452 ));453454 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)455 }456457 458 459 460 461 462 463 464 465 466 467 468 469 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]470 #[transactional]471 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {472 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);473 let collection = <CollectionHandle<T>>::try_get(collection_id)?;474 collection.check_is_internal()?;475476 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(477 collection_id,478 account_id.clone()479 ));480481 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)482 }483484 485 486 487 488 489 490 491 492 493 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]494 #[transactional]495 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {496 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);497498 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;499 target_collection.check_is_owner_or_admin(&sender)?;500 target_collection.check_is_internal()?;501502 target_collection.set_sponsor(new_sponsor.clone())?;503504 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(505 collection_id,506 new_sponsor507 ));508509 target_collection.save()510 }511512 513 514 515 516 517 518 519 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]520 #[transactional]521 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {522 let sender = ensure_signed(origin)?;523524 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;525 target_collection.check_is_internal()?;526 ensure!(527 target_collection.confirm_sponsorship(&sender)?,528 Error::<T>::ConfirmUnsetSponsorFail529 );530531 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(532 collection_id,533 sender534 ));535536 target_collection.save()537 }538539 540 541 542 543 544 545 546 547 548 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]549 #[transactional]550 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {551 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);552553 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;554 target_collection.check_is_internal()?;555 target_collection.check_is_owner(&sender)?;556557 target_collection.sponsorship = SponsorshipState::Disabled;558559 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(560 collection_id561 ));562 target_collection.save()563 }564565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 #[weight = T::CommonWeightInfo::create_item()]584 #[transactional]585 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {586 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);587 let budget = budget::Value::new(NESTING_BUDGET);588589 dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))590 }591592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]611 #[transactional]612 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {613 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);614 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);615 let budget = budget::Value::new(NESTING_BUDGET);616617 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))618 }619620 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]621 #[transactional]622 pub fn set_collection_properties(623 origin,624 collection_id: CollectionId,625 properties: Vec<Property>626 ) -> DispatchResultWithPostInfo {627 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);628629 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);630631 dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))632 }633634 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]635 #[transactional]636 pub fn delete_collection_properties(637 origin,638 collection_id: CollectionId,639 property_keys: Vec<PropertyKey>,640 ) -> DispatchResultWithPostInfo {641 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);642643 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);644645 dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))646 }647648 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]649 #[transactional]650 pub fn set_token_properties(651 origin,652 collection_id: CollectionId,653 token_id: TokenId,654 properties: Vec<Property>655 ) -> DispatchResultWithPostInfo {656 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);657658 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);659660 dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))661 }662663 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]664 #[transactional]665 pub fn delete_token_properties(666 origin,667 collection_id: CollectionId,668 token_id: TokenId,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_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))676 }677678 #[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]679 #[transactional]680 pub fn set_token_property_permissions(681 origin,682 collection_id: CollectionId,683 property_permissions: Vec<PropertyKeyPermission>,684 ) -> DispatchResultWithPostInfo {685 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);686687 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);688689 dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))690 }691692 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]693 #[transactional]694 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {695 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);696 let budget = budget::Value::new(NESTING_BUDGET);697698 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))699 }700701 702 703 704 705 706 707 708 709 710 711 712 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]713 #[transactional]714 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {715 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);716 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;717 target_collection.check_is_internal()?;718 target_collection.check_is_owner(&sender)?;719720 721722 target_collection.limits.transfers_enabled = Some(value);723 target_collection.save()724 }725726 727 728 729 730 731 732 733 734 735 736 737 738 739 #[weight = T::CommonWeightInfo::burn_item()]740 #[transactional]741 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {742 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);743744 let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;745 if value == 1 {746 <NftTransferBasket<T>>::remove(collection_id, item_id);747 <NftApproveBasket<T>>::remove(collection_id, item_id);748 }749 750 751 752 Ok(post_info)753 }754755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 #[weight = T::CommonWeightInfo::burn_from()]772 #[transactional]773 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {774 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);775 let budget = budget::Value::new(NESTING_BUDGET);776777 dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))778 }779780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 #[weight = T::CommonWeightInfo::transfer()]804 #[transactional]805 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {806 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);807 let budget = budget::Value::new(NESTING_BUDGET);808809 dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))810 }811812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 #[weight = T::CommonWeightInfo::approve()]828 #[transactional]829 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {830 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);831832 dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))833 }834835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 #[weight = T::CommonWeightInfo::transfer_from()]855 #[transactional]856 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {857 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);858 let budget = budget::Value::new(NESTING_BUDGET);859860 dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))861 }862863 #[weight = <SelfWeightOf<T>>::set_collection_limits()]864 #[transactional]865 pub fn set_collection_limits(866 origin,867 collection_id: CollectionId,868 new_limit: CollectionLimits,869 ) -> DispatchResult {870 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);871 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;872 target_collection.check_is_internal()?;873 target_collection.check_is_owner_or_admin(&sender)?;874 let old_limit = &target_collection.limits;875876 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;877878 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(879 collection_id880 ));881882 target_collection.save()883 }884885 #[weight = <SelfWeightOf<T>>::set_collection_limits()]886 #[transactional]887 pub fn set_collection_permissions(888 origin,889 collection_id: CollectionId,890 new_limit: CollectionPermissions,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_internal()?;895 target_collection.check_is_owner_or_admin(&sender)?;896 let old_limit = &target_collection.permissions;897898 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;899900 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(901 collection_id902 ));903904 target_collection.save()905 }906907 #[weight = <SelfWeightOf<T>>::set_collection_limits()]908 #[transactional]909 pub fn repartition(910 origin,911 collection_id: CollectionId,912 token: TokenId,913 amount: u128,914 ) -> DispatchResult {915 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);916 let target_collection = <CollectionHandle<T>>::try_get(collection_id)?;917 target_collection.check_is_internal()?;918 let refungible_collection = match target_collection.mode {919 CollectionMode::ReFungible => RefungibleHandle::cast(target_collection),920 _ => fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection),921 };922 <PalletRefungible<T>>::repartition(&sender, &refungible_collection, token, amount)?;923 Ok(())924 }925 }926}