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, RefungibleExtensionsWeightInfo,49};50pub mod eth;5152#[cfg(feature = "runtime-benchmarks")]53mod benchmarking;54pub mod weights;55use weights::WeightInfo;5657const NESTING_BUDGET: u32 = 5;5859decl_error! {60 61 pub enum Error for Module<T: Config> {62 63 CollectionDecimalPointLimitExceeded,64 65 ConfirmUnsetSponsorFail,66 67 EmptyArgument,68 69 RepartitionCalledOnNonRefungibleCollection,70 }71}7273pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {74 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7576 77 type WeightInfo: WeightInfo;78 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;79 type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;80}8182decl_event! {83 pub enum Event<T>84 where85 <T as frame_system::Config>::AccountId,86 <T as pallet_evm::account::Config>::CrossAccountId,87 {88 89 90 91 92 93 CollectionSponsorRemoved(CollectionId),9495 96 97 98 99 100 101 102 CollectionAdminAdded(CollectionId, CrossAccountId),103104 105 106 107 108 109 110 111 CollectionOwnedChanged(CollectionId, AccountId),112113 114 115 116 117 118 119 120 CollectionSponsorSet(CollectionId, AccountId),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),166 }167}168169type SelfWeightOf<T> = <T as Config>::WeightInfo;170171172173174175176177178179180181182183184185186187188189190191192193decl_storage! {194 trait Store for Module<T: Config> as Unique {195196 197 198 ChainVersion: u64;199 200201 202 203 204 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;205 206 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;207 208 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;209 210 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>;211 212213 214 215 #[deprecated]216 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;217 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;218219 220 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;221 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;222 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>;223 }224}225226decl_module! {227 pub struct Module<T: Config> for enum Call228 where229 origin: T::Origin230 {231 type Error = Error<T>;232233 fn deposit_event() = default;234235 fn on_initialize(_now: T::BlockNumber) -> Weight {236 0237 }238239 fn on_runtime_upgrade() -> Weight {240 let limit = None;241242 <VariableMetaDataBasket<T>>::remove_all(limit);243244 0245 }246247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 #[weight = <SelfWeightOf<T>>::create_collection()]264 #[transactional]265 #[deprecated]266 pub fn create_collection(origin,267 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,268 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,269 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,270 mode: CollectionMode) -> DispatchResult {271 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {272 name: collection_name,273 description: collection_description,274 token_prefix,275 mode,276 ..Default::default()277 };278 Self::create_collection_ex(origin, data)279 }280281 282 283 284 #[weight = <SelfWeightOf<T>>::create_collection()]285 #[transactional]286 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {287 let sender = ensure_signed(origin)?;288289 290291 T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;292293 Ok(())294 }295296 297 298 299 300 301 302 303 304 305 #[weight = <SelfWeightOf<T>>::destroy_collection()]306 #[transactional]307 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {308 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);309 let collection = <CollectionHandle<T>>::try_get(collection_id)?;310 collection.check_is_internal()?;311312 313314 T::CollectionDispatch::destroy(sender, collection)?;315316 <NftTransferBasket<T>>::remove_prefix(collection_id, None);317 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);318 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);319320 <NftApproveBasket<T>>::remove_prefix(collection_id, None);321 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);322 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);323324 Ok(())325 }326327 328 329 330 331 332 333 334 335 336 337 338 339 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]340 #[transactional]341 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{342343 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);344 let collection = <CollectionHandle<T>>::try_get(collection_id)?;345 collection.check_is_internal()?;346347 <PalletCommon<T>>::toggle_allowlist(348 &collection,349 &sender,350 &address,351 true,352 )?;353354 Self::deposit_event(Event::<T>::AllowListAddressAdded(355 collection_id,356 address357 ));358359 Ok(())360 }361362 363 364 365 366 367 368 369 370 371 372 373 374 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]375 #[transactional]376 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{377378 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);379 let collection = <CollectionHandle<T>>::try_get(collection_id)?;380 collection.check_is_internal()?;381382 <PalletCommon<T>>::toggle_allowlist(383 &collection,384 &sender,385 &address,386 false,387 )?;388389 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(390 collection_id,391 address392 ));393394 Ok(())395 }396397 398 399 400 401 402 403 404 405 406 407 408 #[weight = <SelfWeightOf<T>>::change_collection_owner()]409 #[transactional]410 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {411412 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);413414 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;415 target_collection.check_is_internal()?;416 target_collection.check_is_owner(&sender)?;417418 target_collection.owner = new_owner.clone();419 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(420 collection_id,421 new_owner422 ));423424 target_collection.save()425 }426427 428 429 430 431 432 433 434 435 436 437 438 439 440 #[weight = <SelfWeightOf<T>>::add_collection_admin()]441 #[transactional]442 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {443 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);444 let collection = <CollectionHandle<T>>::try_get(collection_id)?;445 collection.check_is_internal()?;446447 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(448 collection_id,449 new_admin_id.clone()450 ));451452 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)453 }454455 456 457 458 459 460 461 462 463 464 465 466 467 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]468 #[transactional]469 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {470 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);471 let collection = <CollectionHandle<T>>::try_get(collection_id)?;472 collection.check_is_internal()?;473474 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(475 collection_id,476 account_id.clone()477 ));478479 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)480 }481482 483 484 485 486 487 488 489 490 491 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]492 #[transactional]493 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {494 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);495496 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;497 target_collection.check_is_owner_or_admin(&sender)?;498 target_collection.check_is_internal()?;499500 target_collection.set_sponsor(new_sponsor.clone())?;501502 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(503 collection_id,504 new_sponsor505 ));506507 target_collection.save()508 }509510 511 512 513 514 515 516 517 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]518 #[transactional]519 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {520 let sender = ensure_signed(origin)?;521522 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;523 target_collection.check_is_internal()?;524 ensure!(525 target_collection.confirm_sponsorship(&sender)?,526 Error::<T>::ConfirmUnsetSponsorFail527 );528529 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(530 collection_id,531 sender532 ));533534 target_collection.save()535 }536537 538 539 540 541 542 543 544 545 546 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]547 #[transactional]548 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {549 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);550551 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;552 target_collection.check_is_internal()?;553 target_collection.check_is_owner(&sender)?;554555 target_collection.sponsorship = SponsorshipState::Disabled;556557 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(558 collection_id559 ));560 target_collection.save()561 }562563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 #[weight = T::CommonWeightInfo::create_item()]582 #[transactional]583 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {584 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);585 let budget = budget::Value::new(NESTING_BUDGET);586587 dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))588 }589590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]609 #[transactional]610 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {611 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);612 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);613 let budget = budget::Value::new(NESTING_BUDGET);614615 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))616 }617618 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]619 #[transactional]620 pub fn set_collection_properties(621 origin,622 collection_id: CollectionId,623 properties: Vec<Property>624 ) -> DispatchResultWithPostInfo {625 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);626627 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);628629 dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))630 }631632 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]633 #[transactional]634 pub fn delete_collection_properties(635 origin,636 collection_id: CollectionId,637 property_keys: Vec<PropertyKey>,638 ) -> DispatchResultWithPostInfo {639 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);640641 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);642643 dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))644 }645646 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]647 #[transactional]648 pub fn set_token_properties(649 origin,650 collection_id: CollectionId,651 token_id: TokenId,652 properties: Vec<Property>653 ) -> DispatchResultWithPostInfo {654 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);655656 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);657 let budget = budget::Value::new(NESTING_BUDGET);658659 dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))660 }661662 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]663 #[transactional]664 pub fn delete_token_properties(665 origin,666 collection_id: CollectionId,667 token_id: TokenId,668 property_keys: Vec<PropertyKey>669 ) -> DispatchResultWithPostInfo {670 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);671672 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);673 let budget = budget::Value::new(NESTING_BUDGET);674675 dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))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 = T::RefungibleExtensionsWeightInfo::repartition()]908 #[transactional]909 pub fn repartition(910 origin,911 collection_id: CollectionId,912 token: TokenId,913 amount: u128,914 ) -> DispatchResultWithPostInfo {915 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);916 dispatch_tx::<T, _>(collection_id, |d| {917 if let Some(refungible_extensions) = d.refungible_extensions() {918 refungible_extensions.repartition(&sender, token, amount)919 } else {920 fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)921 }922 })923 }924 }925}