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)?);657658 dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))659 }660661 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]662 #[transactional]663 pub fn delete_token_properties(664 origin,665 collection_id: CollectionId,666 token_id: TokenId,667 property_keys: Vec<PropertyKey>668 ) -> DispatchResultWithPostInfo {669 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);670671 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);672673 dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))674 }675676 #[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]677 #[transactional]678 pub fn set_token_property_permissions(679 origin,680 collection_id: CollectionId,681 property_permissions: Vec<PropertyKeyPermission>,682 ) -> DispatchResultWithPostInfo {683 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);684685 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);686687 dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))688 }689690 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]691 #[transactional]692 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {693 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);694 let budget = budget::Value::new(NESTING_BUDGET);695696 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))697 }698699 700 701 702 703 704 705 706 707 708 709 710 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]711 #[transactional]712 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {713 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);714 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;715 target_collection.check_is_internal()?;716 target_collection.check_is_owner(&sender)?;717718 719720 target_collection.limits.transfers_enabled = Some(value);721 target_collection.save()722 }723724 725 726 727 728 729 730 731 732 733 734 735 736 737 #[weight = T::CommonWeightInfo::burn_item()]738 #[transactional]739 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {740 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);741742 let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;743 if value == 1 {744 <NftTransferBasket<T>>::remove(collection_id, item_id);745 <NftApproveBasket<T>>::remove(collection_id, item_id);746 }747 748 749 750 Ok(post_info)751 }752753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 #[weight = T::CommonWeightInfo::burn_from()]770 #[transactional]771 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {772 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);773 let budget = budget::Value::new(NESTING_BUDGET);774775 dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))776 }777778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 #[weight = T::CommonWeightInfo::transfer()]802 #[transactional]803 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {804 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);805 let budget = budget::Value::new(NESTING_BUDGET);806807 dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))808 }809810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 #[weight = T::CommonWeightInfo::approve()]826 #[transactional]827 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {828 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);829830 dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))831 }832833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 #[weight = T::CommonWeightInfo::transfer_from()]853 #[transactional]854 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {855 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);856 let budget = budget::Value::new(NESTING_BUDGET);857858 dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))859 }860861 #[weight = <SelfWeightOf<T>>::set_collection_limits()]862 #[transactional]863 pub fn set_collection_limits(864 origin,865 collection_id: CollectionId,866 new_limit: CollectionLimits,867 ) -> DispatchResult {868 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);869 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;870 target_collection.check_is_internal()?;871 target_collection.check_is_owner_or_admin(&sender)?;872 let old_limit = &target_collection.limits;873874 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;875876 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(877 collection_id878 ));879880 target_collection.save()881 }882883 #[weight = <SelfWeightOf<T>>::set_collection_limits()]884 #[transactional]885 pub fn set_collection_permissions(886 origin,887 collection_id: CollectionId,888 new_limit: CollectionPermissions,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_internal()?;893 target_collection.check_is_owner_or_admin(&sender)?;894 let old_limit = &target_collection.permissions;895896 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;897898 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(899 collection_id900 ));901902 target_collection.save()903 }904905 #[weight = T::RefungibleExtensionsWeightInfo::repartition()]906 #[transactional]907 pub fn repartition(908 origin,909 collection_id: CollectionId,910 token: TokenId,911 amount: u128,912 ) -> DispatchResultWithPostInfo {913 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);914 dispatch_tx::<T, _>(collection_id, |d| {915 if let Some(refungible_extensions) = d.refungible_extensions() {916 refungible_extensions.repartition(&sender, token, amount)917 } else {918 fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)919 }920 })921 }922 }923}