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, 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_call,48 dispatch::CollectionDispatch,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}7071pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {72 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7374 75 type WeightInfo: WeightInfo;76 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;77}7879decl_event! {80 pub enum Event<T>81 where82 <T as frame_system::Config>::AccountId,83 <T as pallet_evm::account::Config>::CrossAccountId,84 {85 86 87 88 89 90 CollectionSponsorRemoved(CollectionId),9192 93 94 95 96 97 98 99 CollectionAdminAdded(CollectionId, CrossAccountId),100101 102 103 104 105 106 107 108 CollectionOwnedChanged(CollectionId, AccountId),109110 111 112 113 114 115 116 117 CollectionSponsorSet(CollectionId, AccountId),118119 120 121 122 123 124 125 126 SponsorshipConfirmed(CollectionId, AccountId),127128 129 130 131 132 133 134 135 CollectionAdminRemoved(CollectionId, CrossAccountId),136137 138 139 140 141 142 143 144 AllowListAddressRemoved(CollectionId, CrossAccountId),145146 147 148 149 150 151 152 153 AllowListAddressAdded(CollectionId, CrossAccountId),154155 156 157 158 159 160 CollectionLimitSet(CollectionId),161162 CollectionPermissionSet(CollectionId),163 }164}165166type SelfWeightOf<T> = <T as Config>::WeightInfo;167168169170171172173174175176177178179180181182183184185186187188189190decl_storage! {191 trait Store for Module<T: Config> as Unique {192193 194 195 ChainVersion: u64;196 197198 199 200 201 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;202 203 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;204 205 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;206 207 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>;208 209210 211 212 #[deprecated]213 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;214 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;215216 217 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;218 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;219 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>;220 }221}222223decl_module! {224 pub struct Module<T: Config> for enum Call225 where226 origin: T::Origin227 {228 type Error = Error<T>;229230 fn deposit_event() = default;231232 fn on_initialize(_now: T::BlockNumber) -> Weight {233 0234 }235236 fn on_runtime_upgrade() -> Weight {237 let limit = None;238239 <VariableMetaDataBasket<T>>::remove_all(limit);240241 0242 }243244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 #[weight = <SelfWeightOf<T>>::create_collection()]261 #[transactional]262 #[deprecated]263 pub fn create_collection(origin,264 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,265 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,266 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,267 mode: CollectionMode) -> DispatchResult {268 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {269 name: collection_name,270 description: collection_description,271 token_prefix,272 mode,273 ..Default::default()274 };275 Self::create_collection_ex(origin, data)276 }277278 279 280 281 #[weight = <SelfWeightOf<T>>::create_collection()]282 #[transactional]283 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {284 let sender = ensure_signed(origin)?;285286 287288 T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;289290 Ok(())291 }292293 294 295 296 297 298 299 300 301 302 #[weight = <SelfWeightOf<T>>::destroy_collection()]303 #[transactional]304 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {305 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);306 let collection = <CollectionHandle<T>>::try_get(collection_id)?;307 collection.check_is_mutable()?;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_mutable()?;411 target_collection.check_is_owner(&sender)?;412413 target_collection.owner = new_owner.clone();414 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(415 collection_id,416 new_owner417 ));418419 target_collection.save()420 }421422 423 424 425 426 427 428 429 430 431 432 433 434 435 #[weight = <SelfWeightOf<T>>::add_collection_admin()]436 #[transactional]437 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {438 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);439 let collection = <CollectionHandle<T>>::try_get(collection_id)?;440441 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(442 collection_id,443 new_admin_id.clone()444 ));445446 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)447 }448449 450 451 452 453 454 455 456 457 458 459 460 461 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]462 #[transactional]463 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {464 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);465 let collection = <CollectionHandle<T>>::try_get(collection_id)?;466467 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(468 collection_id,469 account_id.clone()470 ));471472 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)473 }474475 476 477 478 479 480 481 482 483 484 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]485 #[transactional]486 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {487 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);488489 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;490 target_collection.check_is_owner(&sender)?;491492 target_collection.set_sponsor(new_sponsor.clone())?;493494 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(495 collection_id,496 new_sponsor497 ));498499 target_collection.save()500 }501502 503 504 505 506 507 508 509 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]510 #[transactional]511 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {512 let sender = ensure_signed(origin)?;513514 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;515 ensure!(516 target_collection.confirm_sponsorship(&sender)?,517 Error::<T>::ConfirmUnsetSponsorFail518 );519520 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(521 collection_id,522 sender523 ));524525 target_collection.save()526 }527528 529 530 531 532 533 534 535 536 537 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]538 #[transactional]539 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {540 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);541542 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;543 target_collection.check_is_owner(&sender)?;544545 target_collection.sponsorship = SponsorshipState::Disabled;546547 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(548 collection_id549 ));550 target_collection.save()551 }552553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 #[weight = T::CommonWeightInfo::create_item()]572 #[transactional]573 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {574 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);575 let budget = budget::Value::new(NESTING_BUDGET);576577 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))578 }579580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]599 #[transactional]600 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {601 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);602 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);603 let budget = budget::Value::new(NESTING_BUDGET);604605 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))606 }607608 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]609 #[transactional]610 pub fn set_collection_properties(611 origin,612 collection_id: CollectionId,613 properties: Vec<Property>614 ) -> DispatchResultWithPostInfo {615 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);616617 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);618619 dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))620 }621622 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]623 #[transactional]624 pub fn delete_collection_properties(625 origin,626 collection_id: CollectionId,627 property_keys: Vec<PropertyKey>,628 ) -> DispatchResultWithPostInfo {629 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);630631 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);632633 dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))634 }635636 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]637 #[transactional]638 pub fn set_token_properties(639 origin,640 collection_id: CollectionId,641 token_id: TokenId,642 properties: Vec<Property>643 ) -> DispatchResultWithPostInfo {644 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);645646 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);647648 dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))649 }650651 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]652 #[transactional]653 pub fn delete_token_properties(654 origin,655 collection_id: CollectionId,656 token_id: TokenId,657 property_keys: Vec<PropertyKey>658 ) -> DispatchResultWithPostInfo {659 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);660661 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);662663 dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))664 }665666 #[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]667 #[transactional]668 pub fn set_property_permissions(669 origin,670 collection_id: CollectionId,671 property_permissions: Vec<PropertyKeyPermission>,672 ) -> DispatchResultWithPostInfo {673 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);674675 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);676677 dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))678 }679680 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]681 #[transactional]682 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {683 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);684 let budget = budget::Value::new(NESTING_BUDGET);685686 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))687 }688689 690691 692 693 694 695 696 697 698 699 700 701 702 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]703 #[transactional]704 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {705 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);706 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;707 target_collection.check_is_owner(&sender)?;708709 710711 target_collection.limits.transfers_enabled = Some(value);712 target_collection.save()713 }714715 716 717 718 719 720 721 722 723 724 725 726 727 728 #[weight = T::CommonWeightInfo::burn_item()]729 #[transactional]730 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {731 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);732733 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;734 if value == 1 {735 <NftTransferBasket<T>>::remove(collection_id, item_id);736 <NftApproveBasket<T>>::remove(collection_id, item_id);737 }738 739 740 741 Ok(post_info)742 }743744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 #[weight = T::CommonWeightInfo::burn_from()]761 #[transactional]762 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {763 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);764 let budget = budget::Value::new(NESTING_BUDGET);765766 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))767 }768769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 #[weight = T::CommonWeightInfo::transfer()]793 #[transactional]794 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {795 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);796 let budget = budget::Value::new(NESTING_BUDGET);797798 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))799 }800801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 #[weight = T::CommonWeightInfo::approve()]817 #[transactional]818 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {819 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);820821 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))822 }823824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 #[weight = T::CommonWeightInfo::transfer_from()]844 #[transactional]845 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {846 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);847 let budget = budget::Value::new(NESTING_BUDGET);848849 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))850 }851852 #[weight = <SelfWeightOf<T>>::set_collection_limits()]853 #[transactional]854 pub fn set_collection_limits(855 origin,856 collection_id: CollectionId,857 new_limit: CollectionLimits,858 ) -> DispatchResult {859 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);860 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;861 target_collection.check_is_owner(&sender)?;862 let old_limit = &target_collection.limits;863864 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;865866 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(867 collection_id868 ));869870 target_collection.save()871 }872873 #[weight = <SelfWeightOf<T>>::set_collection_limits()]874 #[transactional]875 pub fn set_collection_permissions(876 origin,877 collection_id: CollectionId,878 new_limit: CollectionPermissions,879 ) -> DispatchResult {880 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);881 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;882 target_collection.check_is_owner(&sender)?;883 let old_limit = &target_collection.permissions;884885 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;886887 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(888 collection_id889 ));890891 target_collection.save()892 }893 }894}