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 AccessMode, CreateItemData, CollectionLimits, CollectionPermissions, CollectionId,42 CollectionMode, TokenId, SponsorshipState, CreateCollectionData, CreateItemExData, budget,43 Property, PropertyKey, 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(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)?;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)?;341342 <PalletCommon<T>>::toggle_allowlist(343 &collection,344 &sender,345 &address,346 true,347 )?;348349 Self::deposit_event(Event::<T>::AllowListAddressAdded(350 collection_id,351 address352 ));353354 Ok(())355 }356357 358 359 360 361 362 363 364 365 366 367 368 369 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]370 #[transactional]371 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{372373 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);374 let collection = <CollectionHandle<T>>::try_get(collection_id)?;375376 <PalletCommon<T>>::toggle_allowlist(377 &collection,378 &sender,379 &address,380 false,381 )?;382383 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(384 collection_id,385 address386 ));387388 Ok(())389 }390391 392 393 394 395 396 397 398 399 400 401 402 #[weight = <SelfWeightOf<T>>::change_collection_owner()]403 #[transactional]404 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {405406 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);407408 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;409 target_collection.check_is_owner(&sender)?;410411 target_collection.owner = new_owner.clone();412 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(413 collection_id,414 new_owner415 ));416417 target_collection.save()418 }419420 421 422 423 424 425 426 427 428 429 430 431 432 433 #[weight = <SelfWeightOf<T>>::add_collection_admin()]434 #[transactional]435 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {436 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);437 let collection = <CollectionHandle<T>>::try_get(collection_id)?;438439 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(440 collection_id,441 new_admin_id.clone()442 ));443444 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)445 }446447 448 449 450 451 452 453 454 455 456 457 458 459 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]460 #[transactional]461 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {462 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);463 let collection = <CollectionHandle<T>>::try_get(collection_id)?;464465 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(466 collection_id,467 account_id.clone()468 ));469470 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)471 }472473 474 475 476 477 478 479 480 481 482 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]483 #[transactional]484 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {485 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);486487 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;488 target_collection.check_is_owner(&sender)?;489490 target_collection.set_sponsor(new_sponsor.clone());491492 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(493 collection_id,494 new_sponsor495 ));496497 target_collection.save()498 }499500 501 502 503 504 505 506 507 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]508 #[transactional]509 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {510 let sender = ensure_signed(origin)?;511512 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;513 ensure!(514 target_collection.confirm_sponsorship(&sender),515 Error::<T>::ConfirmUnsetSponsorFail516 );517518 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(519 collection_id,520 sender521 ));522523 target_collection.save()524 }525526 527 528 529 530 531 532 533 534 535 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]536 #[transactional]537 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {538 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);539540 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;541 target_collection.check_is_owner(&sender)?;542543 target_collection.sponsorship = SponsorshipState::Disabled;544545 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(546 collection_id547 ));548 target_collection.save()549 }550551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 #[weight = T::CommonWeightInfo::create_item()]570 #[transactional]571 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {572 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);573 let budget = budget::Value::new(NESTING_BUDGET);574575 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))576 }577578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]597 #[transactional]598 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {599 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);600 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);601 let budget = budget::Value::new(NESTING_BUDGET);602603 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))604 }605606 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]607 #[transactional]608 pub fn set_collection_properties(609 origin,610 collection_id: CollectionId,611 properties: Vec<Property>612 ) -> DispatchResultWithPostInfo {613 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);614615 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);616617 dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))618 }619620 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]621 #[transactional]622 pub fn delete_collection_properties(623 origin,624 collection_id: CollectionId,625 property_keys: Vec<PropertyKey>,626 ) -> DispatchResultWithPostInfo {627 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);628629 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);630631 dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))632 }633634 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]635 #[transactional]636 pub fn set_token_properties(637 origin,638 collection_id: CollectionId,639 token_id: TokenId,640 properties: Vec<Property>641 ) -> DispatchResultWithPostInfo {642 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);643644 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);645646 dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))647 }648649 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]650 #[transactional]651 pub fn delete_token_properties(652 origin,653 collection_id: CollectionId,654 token_id: TokenId,655 property_keys: Vec<PropertyKey>656 ) -> DispatchResultWithPostInfo {657 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);658659 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);660661 dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))662 }663664 #[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]665 #[transactional]666 pub fn set_property_permissions(667 origin,668 collection_id: CollectionId,669 property_permissions: Vec<PropertyKeyPermission>,670 ) -> DispatchResultWithPostInfo {671 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);672673 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);674675 dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))676 }677678 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]679 #[transactional]680 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {681 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);682 let budget = budget::Value::new(NESTING_BUDGET);683684 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))685 }686687 688689 690 691 692 693 694 695 696 697 698 699 700 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]701 #[transactional]702 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {703 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);704 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;705 target_collection.check_is_owner(&sender)?;706707 708709 target_collection.limits.transfers_enabled = Some(value);710 target_collection.save()711 }712713 714 715 716 717 718 719 720 721 722 723 724 725 726 #[weight = T::CommonWeightInfo::burn_item()]727 #[transactional]728 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {729 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);730731 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;732 if value == 1 {733 <NftTransferBasket<T>>::remove(collection_id, item_id);734 <NftApproveBasket<T>>::remove(collection_id, item_id);735 }736 737 738 739 Ok(post_info)740 }741742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 #[weight = T::CommonWeightInfo::burn_from()]759 #[transactional]760 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {761 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);762 let budget = budget::Value::new(NESTING_BUDGET);763764 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))765 }766767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 #[weight = T::CommonWeightInfo::transfer()]791 #[transactional]792 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {793 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);794 let budget = budget::Value::new(NESTING_BUDGET);795796 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))797 }798799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 #[weight = T::CommonWeightInfo::approve()]815 #[transactional]816 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {817 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);818819 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))820 }821822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 #[weight = T::CommonWeightInfo::transfer_from()]842 #[transactional]843 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {844 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);845 let budget = budget::Value::new(NESTING_BUDGET);846847 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))848 }849850 #[weight = <SelfWeightOf<T>>::set_collection_limits()]851 #[transactional]852 pub fn set_collection_limits(853 origin,854 collection_id: CollectionId,855 new_limit: CollectionLimits,856 ) -> DispatchResult {857 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);858 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;859 target_collection.check_is_owner(&sender)?;860 let old_limit = &target_collection.limits;861862 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;863864 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(865 collection_id866 ));867868 target_collection.save()869 }870871 #[weight = <SelfWeightOf<T>>::set_collection_limits()]872 #[transactional]873 pub fn set_collection_permissions(874 origin,875 collection_id: CollectionId,876 new_limit: CollectionPermissions,877 ) -> DispatchResult {878 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);879 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;880 target_collection.check_is_owner(&sender)?;881 let old_limit = &target_collection.permissions;882883 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;884885 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(886 collection_id887 ));888889 target_collection.save()890 }891 }892}