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;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}7172pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {73 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7475 76 type WeightInfo: WeightInfo;77 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;78}7980decl_event! {81 pub enum Event<T>82 where83 <T as frame_system::Config>::AccountId,84 <T as pallet_evm::account::Config>::CrossAccountId,85 {86 87 88 89 90 91 CollectionSponsorRemoved(CollectionId),9293 94 95 96 97 98 99 100 CollectionAdminAdded(CollectionId, CrossAccountId),101102 103 104 105 106 107 108 109 CollectionOwnedChanged(CollectionId, AccountId),110111 112 113 114 115 116 117 118 CollectionSponsorSet(CollectionId, AccountId),119120 121 122 123 124 125 126 127 SponsorshipConfirmed(CollectionId, AccountId),128129 130 131 132 133 134 135 136 CollectionAdminRemoved(CollectionId, CrossAccountId),137138 139 140 141 142 143 144 145 AllowListAddressRemoved(CollectionId, CrossAccountId),146147 148 149 150 151 152 153 154 AllowListAddressAdded(CollectionId, CrossAccountId),155156 157 158 159 160 161 CollectionLimitSet(CollectionId),162163 CollectionPermissionSet(CollectionId),164 }165}166167type SelfWeightOf<T> = <T as Config>::WeightInfo;168169170171172173174175176177178179180181182183184185186187188189190191decl_storage! {192 trait Store for Module<T: Config> as Unique {193194 195 196 ChainVersion: u64;197 198199 200 201 202 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;203 204 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;205 206 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;207 208 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>;209 210211 212 213 #[deprecated]214 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;215 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;216217 218 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;219 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;220 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>;221 }222}223224decl_module! {225 pub struct Module<T: Config> for enum Call226 where227 origin: T::Origin228 {229 type Error = Error<T>;230231 fn deposit_event() = default;232233 fn on_initialize(_now: T::BlockNumber) -> Weight {234 0235 }236237 fn on_runtime_upgrade() -> Weight {238 let limit = None;239240 <VariableMetaDataBasket<T>>::remove_all(limit);241242 0243 }244245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 #[weight = <SelfWeightOf<T>>::create_collection()]262 #[transactional]263 #[deprecated]264 pub fn create_collection(origin,265 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,266 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,267 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,268 mode: CollectionMode) -> DispatchResult {269 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {270 name: collection_name,271 description: collection_description,272 token_prefix,273 mode,274 ..Default::default()275 };276 Self::create_collection_ex(origin, data)277 }278279 280 281 282 #[weight = <SelfWeightOf<T>>::create_collection()]283 #[transactional]284 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {285 let sender = ensure_signed(origin)?;286287 288289 T::CollectionDispatch::create(sender, data)?;290291 Ok(())292 }293294 295 296 297 298 299 300 301 302 303 #[weight = <SelfWeightOf<T>>::destroy_collection()]304 #[transactional]305 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {306 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);307 let collection = <CollectionHandle<T>>::try_get(collection_id)?;308309 310311 T::CollectionDispatch::destroy(sender, collection)?;312313 <NftTransferBasket<T>>::remove_prefix(collection_id, None);314 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);315 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);316317 <NftApproveBasket<T>>::remove_prefix(collection_id, None);318 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);319 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);320321 Ok(())322 }323324 325 326 327 328 329 330 331 332 333 334 335 336 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]337 #[transactional]338 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{339340 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);341 let collection = <CollectionHandle<T>>::try_get(collection_id)?;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)?;376377 <PalletCommon<T>>::toggle_allowlist(378 &collection,379 &sender,380 &address,381 false,382 )?;383384 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(385 collection_id,386 address387 ));388389 Ok(())390 }391392 393 394 395 396 397 398 399 400 401 402 403 #[weight = <SelfWeightOf<T>>::change_collection_owner()]404 #[transactional]405 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {406407 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);408409 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;410 target_collection.check_is_owner(&sender)?;411412 target_collection.owner = new_owner.clone();413 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(414 collection_id,415 new_owner416 ));417418 target_collection.save()419 }420421 422 423 424 425 426 427 428 429 430 431 432 433 434 #[weight = <SelfWeightOf<T>>::add_collection_admin()]435 #[transactional]436 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {437 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);438 let collection = <CollectionHandle<T>>::try_get(collection_id)?;439440 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(441 collection_id,442 new_admin_id.clone()443 ));444445 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)446 }447448 449 450 451 452 453 454 455 456 457 458 459 460 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]461 #[transactional]462 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {463 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);464 let collection = <CollectionHandle<T>>::try_get(collection_id)?;465466 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(467 collection_id,468 account_id.clone()469 ));470471 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)472 }473474 475 476 477 478 479 480 481 482 483 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]484 #[transactional]485 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {486 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);487488 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;489 target_collection.check_is_owner(&sender)?;490491 target_collection.set_sponsor(new_sponsor.clone());492493 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(494 collection_id,495 new_sponsor496 ));497498 target_collection.save()499 }500501 502 503 504 505 506 507 508 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]509 #[transactional]510 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {511 let sender = ensure_signed(origin)?;512513 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;514 ensure!(515 target_collection.confirm_sponsorship(&sender),516 Error::<T>::ConfirmUnsetSponsorFail517 );518519 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(520 collection_id,521 sender522 ));523524 target_collection.save()525 }526527 528 529 530 531 532 533 534 535 536 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]537 #[transactional]538 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {539 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);540541 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;542 target_collection.check_is_owner(&sender)?;543544 target_collection.sponsorship = SponsorshipState::Disabled;545546 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(547 collection_id548 ));549 target_collection.save()550 }551552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 #[weight = T::CommonWeightInfo::create_item()]571 #[transactional]572 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {573 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);574 let budget = budget::Value::new(NESTING_BUDGET);575576 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))577 }578579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]598 #[transactional]599 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {600 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);601 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);602 let budget = budget::Value::new(NESTING_BUDGET);603604 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))605 }606607 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]608 #[transactional]609 pub fn set_collection_properties(610 origin,611 collection_id: CollectionId,612 properties: Vec<Property>613 ) -> DispatchResultWithPostInfo {614 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);615616 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);617618 dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))619 }620621 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]622 #[transactional]623 pub fn delete_collection_properties(624 origin,625 collection_id: CollectionId,626 property_keys: Vec<PropertyKey>,627 ) -> DispatchResultWithPostInfo {628 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);629630 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);631632 dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))633 }634635 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]636 #[transactional]637 pub fn set_token_properties(638 origin,639 collection_id: CollectionId,640 token_id: TokenId,641 properties: Vec<Property>642 ) -> DispatchResultWithPostInfo {643 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);644645 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);646647 dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))648 }649650 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]651 #[transactional]652 pub fn delete_token_properties(653 origin,654 collection_id: CollectionId,655 token_id: TokenId,656 property_keys: Vec<PropertyKey>657 ) -> DispatchResultWithPostInfo {658 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);659660 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);661662 dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))663 }664665 #[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]666 #[transactional]667 pub fn set_property_permissions(668 origin,669 collection_id: CollectionId,670 property_permissions: Vec<PropertyKeyPermission>,671 ) -> DispatchResultWithPostInfo {672 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);673674 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);675676 dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))677 }678679 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]680 #[transactional]681 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {682 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);683 let budget = budget::Value::new(NESTING_BUDGET);684685 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))686 }687688 689690 691 692 693 694 695 696 697 698 699 700 701 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]702 #[transactional]703 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {704 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);705 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;706 target_collection.check_is_owner(&sender)?;707708 709710 target_collection.limits.transfers_enabled = Some(value);711 target_collection.save()712 }713714 715 716 717 718 719 720 721 722 723 724 725 726 727 #[weight = T::CommonWeightInfo::burn_item()]728 #[transactional]729 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {730 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);731732 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;733 if value == 1 {734 <NftTransferBasket<T>>::remove(collection_id, item_id);735 <NftApproveBasket<T>>::remove(collection_id, item_id);736 }737 738 739 740 Ok(post_info)741 }742743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 #[weight = T::CommonWeightInfo::burn_from()]760 #[transactional]761 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {762 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);763 let budget = budget::Value::new(NESTING_BUDGET);764765 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))766 }767768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 #[weight = T::CommonWeightInfo::transfer()]792 #[transactional]793 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {794 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);795 let budget = budget::Value::new(NESTING_BUDGET);796797 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))798 }799800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 #[weight = T::CommonWeightInfo::approve()]816 #[transactional]817 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {818 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);819820 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))821 }822823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 #[weight = T::CommonWeightInfo::transfer_from()]843 #[transactional]844 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {845 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);846 let budget = budget::Value::new(NESTING_BUDGET);847848 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))849 }850851 #[weight = <SelfWeightOf<T>>::set_collection_limits()]852 #[transactional]853 pub fn set_collection_limits(854 origin,855 collection_id: CollectionId,856 new_limit: CollectionLimits,857 ) -> DispatchResult {858 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);859 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;860 target_collection.check_is_owner(&sender)?;861 let old_limit = &target_collection.limits;862863 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;864865 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(866 collection_id867 ));868869 target_collection.save()870 }871872 #[weight = <SelfWeightOf<T>>::set_collection_limits()]873 #[transactional]874 pub fn set_collection_permissions(875 origin,876 collection_id: CollectionId,877 new_limit: CollectionPermissions,878 ) -> DispatchResult {879 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);880 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;881 target_collection.check_is_owner(&sender)?;882 let old_limit = &target_collection.permissions;883884 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;885886 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(887 collection_id888 ));889890 target_collection.save()891 }892 }893}