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 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(T::CrossAccountId::from_sub(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)?;306 collection.check_is_internal()?;307308 309310 T::CollectionDispatch::destroy(sender, collection)?;311312 <NftTransferBasket<T>>::remove_prefix(collection_id, None);313 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);314 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);315316 <NftApproveBasket<T>>::remove_prefix(collection_id, None);317 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);318 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);319320 Ok(())321 }322323 324 325 326 327 328 329 330 331 332 333 334 335 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]336 #[transactional]337 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{338339 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);340 let collection = <CollectionHandle<T>>::try_get(collection_id)?;341 collection.check_is_internal()?;342343 <PalletCommon<T>>::toggle_allowlist(344 &collection,345 &sender,346 &address,347 true,348 )?;349350 Self::deposit_event(Event::<T>::AllowListAddressAdded(351 collection_id,352 address353 ));354355 Ok(())356 }357358 359 360 361 362 363 364 365 366 367 368 369 370 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]371 #[transactional]372 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{373374 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);375 let collection = <CollectionHandle<T>>::try_get(collection_id)?;376 collection.check_is_internal()?;377378 <PalletCommon<T>>::toggle_allowlist(379 &collection,380 &sender,381 &address,382 false,383 )?;384385 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(386 collection_id,387 address388 ));389390 Ok(())391 }392393 394 395 396 397 398 399 400 401 402 403 404 #[weight = <SelfWeightOf<T>>::change_collection_owner()]405 #[transactional]406 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {407408 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);409410 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;411 target_collection.check_is_internal()?;412 target_collection.check_is_owner(&sender)?;413414 target_collection.owner = new_owner.clone();415 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(416 collection_id,417 new_owner418 ));419420 target_collection.save()421 }422423 424 425 426 427 428 429 430 431 432 433 434 435 436 #[weight = <SelfWeightOf<T>>::add_collection_admin()]437 #[transactional]438 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {439 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);440 let collection = <CollectionHandle<T>>::try_get(collection_id)?;441 collection.check_is_internal()?;442443 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(444 collection_id,445 new_admin_id.clone()446 ));447448 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)449 }450451 452 453 454 455 456 457 458 459 460 461 462 463 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]464 #[transactional]465 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {466 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);467 let collection = <CollectionHandle<T>>::try_get(collection_id)?;468 collection.check_is_internal()?;469470 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(471 collection_id,472 account_id.clone()473 ));474475 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)476 }477478 479 480 481 482 483 484 485 486 487 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]488 #[transactional]489 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {490 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);491492 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;493 target_collection.check_is_owner_or_admin(&sender)?;494 target_collection.check_is_internal()?;495496 target_collection.set_sponsor(new_sponsor.clone())?;497498 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(499 collection_id,500 new_sponsor501 ));502503 target_collection.save()504 }505506 507 508 509 510 511 512 513 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]514 #[transactional]515 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {516 let sender = ensure_signed(origin)?;517518 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;519 target_collection.check_is_internal()?;520 ensure!(521 target_collection.confirm_sponsorship(&sender)?,522 Error::<T>::ConfirmUnsetSponsorFail523 );524525 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(526 collection_id,527 sender528 ));529530 target_collection.save()531 }532533 534 535 536 537 538 539 540 541 542 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]543 #[transactional]544 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {545 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);546547 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;548 target_collection.check_is_internal()?;549 target_collection.check_is_owner(&sender)?;550551 target_collection.sponsorship = SponsorshipState::Disabled;552553 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(554 collection_id555 ));556 target_collection.save()557 }558559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 #[weight = T::CommonWeightInfo::create_item()]578 #[transactional]579 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {580 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);581 let budget = budget::Value::new(NESTING_BUDGET);582583 dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))584 }585586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]605 #[transactional]606 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {607 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);608 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);609 let budget = budget::Value::new(NESTING_BUDGET);610611 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))612 }613614 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]615 #[transactional]616 pub fn set_collection_properties(617 origin,618 collection_id: CollectionId,619 properties: Vec<Property>620 ) -> DispatchResultWithPostInfo {621 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);622623 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);624625 dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))626 }627628 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]629 #[transactional]630 pub fn delete_collection_properties(631 origin,632 collection_id: CollectionId,633 property_keys: Vec<PropertyKey>,634 ) -> DispatchResultWithPostInfo {635 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);636637 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);638639 dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))640 }641642 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]643 #[transactional]644 pub fn set_token_properties(645 origin,646 collection_id: CollectionId,647 token_id: TokenId,648 properties: Vec<Property>649 ) -> DispatchResultWithPostInfo {650 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);651652 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);653 let budget = budget::Value::new(NESTING_BUDGET);654655 dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))656 }657658 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]659 #[transactional]660 pub fn delete_token_properties(661 origin,662 collection_id: CollectionId,663 token_id: TokenId,664 property_keys: Vec<PropertyKey>665 ) -> DispatchResultWithPostInfo {666 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);667668 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);669 let budget = budget::Value::new(NESTING_BUDGET);670671 dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))672 }673674 #[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]675 #[transactional]676 pub fn set_token_property_permissions(677 origin,678 collection_id: CollectionId,679 property_permissions: Vec<PropertyKeyPermission>,680 ) -> DispatchResultWithPostInfo {681 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);682683 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);684685 dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))686 }687688 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]689 #[transactional]690 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {691 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);692 let budget = budget::Value::new(NESTING_BUDGET);693694 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))695 }696697 698 699 700 701 702 703 704 705 706 707 708 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]709 #[transactional]710 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {711 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);712 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;713 target_collection.check_is_internal()?;714 target_collection.check_is_owner(&sender)?;715716 717718 target_collection.limits.transfers_enabled = Some(value);719 target_collection.save()720 }721722 723 724 725 726 727 728 729 730 731 732 733 734 735 #[weight = T::CommonWeightInfo::burn_item()]736 #[transactional]737 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {738 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);739740 let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;741 if value == 1 {742 <NftTransferBasket<T>>::remove(collection_id, item_id);743 <NftApproveBasket<T>>::remove(collection_id, item_id);744 }745 746 747 748 Ok(post_info)749 }750751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 #[weight = T::CommonWeightInfo::burn_from()]768 #[transactional]769 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {770 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);771 let budget = budget::Value::new(NESTING_BUDGET);772773 dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))774 }775776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 #[weight = T::CommonWeightInfo::transfer()]800 #[transactional]801 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {802 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);803 let budget = budget::Value::new(NESTING_BUDGET);804805 dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))806 }807808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 #[weight = T::CommonWeightInfo::approve()]824 #[transactional]825 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {826 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);827828 dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))829 }830831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 #[weight = T::CommonWeightInfo::transfer_from()]851 #[transactional]852 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {853 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);854 let budget = budget::Value::new(NESTING_BUDGET);855856 dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))857 }858859 #[weight = <SelfWeightOf<T>>::set_collection_limits()]860 #[transactional]861 pub fn set_collection_limits(862 origin,863 collection_id: CollectionId,864 new_limit: CollectionLimits,865 ) -> DispatchResult {866 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);867 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;868 target_collection.check_is_internal()?;869 target_collection.check_is_owner_or_admin(&sender)?;870 let old_limit = &target_collection.limits;871872 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;873874 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(875 collection_id876 ));877878 target_collection.save()879 }880881 #[weight = <SelfWeightOf<T>>::set_collection_limits()]882 #[transactional]883 pub fn set_collection_permissions(884 origin,885 collection_id: CollectionId,886 new_limit: CollectionPermissions,887 ) -> DispatchResult {888 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);889 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;890 target_collection.check_is_internal()?;891 target_collection.check_is_owner_or_admin(&sender)?;892 let old_limit = &target_collection.permissions;893894 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;895896 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(897 collection_id898 ));899900 target_collection.save()901 }902903 #[weight = T::RefungibleExtensionsWeightInfo::repartition()]904 #[transactional]905 pub fn repartition(906 origin,907 collection_id: CollectionId,908 token: TokenId,909 amount: u128,910 ) -> DispatchResultWithPostInfo {911 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);912 dispatch_tx::<T, _>(collection_id, |d| {913 if let Some(refungible_extensions) = d.refungible_extensions() {914 refungible_extensions.repartition(&sender, token, amount)915 } else {916 fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)917 }918 })919 }920 }921}