123456#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9 clippy::too_many_arguments,10 clippy::unnecessary_mut_passed,11 clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19 construct_runtime, decl_module, decl_storage, decl_error,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness,24 IsSubType, WithdrawReasons,25 },26 weights::{27 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29 WeightToFeePolynomial, DispatchClass,30 },31 StorageValue, transactional,32 pallet_prelude::DispatchResultWithPostInfo,33};34use scale_info::TypeInfo;35use frame_system::{self as system, ensure_signed};36use sp_runtime::{sp_std::prelude::Vec};37use nft_data_structs::{38 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, CUSTOM_DATA_LIMIT,39 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,40 OFFCHAIN_SCHEMA_LIMIT, AccessMode, Collection, CreateItemData, CollectionLimits, CollectionId,41 CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,42};43use pallet_common::{44 account::CrossAccountId, CollectionHandle, IsAdmin, Pallet as PalletCommon,45 Error as CommonError, CommonWeightInfo, Allowlist,46};47use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};48use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};49use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};5051#[cfg(test)]52mod mock;5354#[cfg(test)]55mod tests;5657mod eth;58mod sponsorship;59pub use sponsorship::NftSponsorshipHandler;60pub use eth::sponsoring::NftEthSponsorshipHandler;6162pub use eth::NftErcSupport;6364pub mod common;65use common::CommonWeights;66pub mod dispatch;67use dispatch::dispatch_call;6869#[cfg(feature = "runtime-benchmarks")]70mod benchmarking;71pub mod weights;72use weights::WeightInfo;7374decl_error! {75 76 pub enum Error for Module<T: Config> {77 78 CollectionDecimalPointLimitExceeded,79 80 ConfirmUnsetSponsorFail,81 82 EmptyArgument,83 84 CollectionLimitBoundsExceeded,85 86 OwnerPermissionsCantBeReverted,87 }88}89pub trait Config:90 system::Config91 + pallet_evm_coder_substrate::Config92 + pallet_common::Config93 + pallet_nonfungible::Config94 + pallet_refungible::Config95 + pallet_fungible::Config96 + Sized97 + TypeInfo98{99 100 type WeightInfo: WeightInfo;101}102103type SelfWeightOf<T> = <T as Config>::WeightInfo;104105106107108109110111112113114115116117118119120121122123124125126127decl_storage! {128 trait Store for Module<T: Config> as Nft {129130 131 132 ChainVersion: u64;133 134135 136 137 138 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;139 140 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;141 142 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;143 144 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;145 146147 148 149 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;150 }151}152153decl_module! {154 pub struct Module<T: Config> for enum Call155 where156 origin: T::Origin157 {158 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;159 type Error = Error<T>;160161 fn on_initialize(_now: T::BlockNumber) -> Weight {162 0163 }164165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 #[weight = <SelfWeightOf<T>>::create_collection()]182 #[transactional]183 pub fn create_collection(origin,184 collection_name: Vec<u16>,185 collection_description: Vec<u16>,186 token_prefix: Vec<u8>,187 mode: CollectionMode) -> DispatchResult {188189 190 let who = ensure_signed(origin)?;191192 let limits = CollectionLimits::<T::BlockNumber> {193 sponsored_data_size: CUSTOM_DATA_LIMIT,194 ..Default::default()195 };196197 198 let new_collection = Collection::<T> {199 owner: who.clone(),200 name: collection_name,201 mode: mode.clone(),202 mint_mode: false,203 access: AccessMode::Normal,204 description: collection_description,205 token_prefix,206 offchain_schema: Vec::new(),207 schema_version: SchemaVersion::ImageURL,208 sponsorship: SponsorshipState::Disabled,209 variable_on_chain_schema: Vec::new(),210 const_on_chain_schema: Vec::new(),211 limits,212 transfers_enabled: true,213 meta_update_permission: Default::default(),214 };215216 let _id = match mode {217 CollectionMode::NFT => {PalletNonfungible::init_collection(new_collection)?},218 CollectionMode::Fungible(decimal_points) => {219 220 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);221 PalletFungible::init_collection(new_collection)?222 }223 CollectionMode::ReFungible => {224 PalletRefungible::init_collection(new_collection)?225 }226 };227228 Ok(())229 }230231 232 233 234 235 236 237 238 239 240 #[weight = <SelfWeightOf<T>>::destroy_collection()]241 #[transactional]242 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {243 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);244245 let collection = <CollectionHandle<T>>::try_get(collection_id)?;246 collection.check_is_owner(&sender)?;247248 249250 match collection.mode {251 CollectionMode::ReFungible => PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?,252 CollectionMode::Fungible(_) => PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?,253 CollectionMode::NFT => PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?,254 }255256 <NftTransferBasket<T>>::remove_prefix(collection_id, None);257 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);258 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);259260 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);261262 Ok(())263 }264265 266 267 268 269 270 271 272 273 274 275 276 277 #[weight = <SelfWeightOf<T>>::add_to_white_list()]278 #[transactional]279 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{280281 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);282 let collection = <CollectionHandle<T>>::try_get(collection_id)?;283284 <PalletCommon<T>>::toggle_allowlist(285 &collection,286 &sender,287 &address,288 true,289 )?;290291 Ok(())292 }293294 295 296 297 298 299 300 301 302 303 304 305 306 #[weight = <SelfWeightOf<T>>::remove_from_white_list()]307 #[transactional]308 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{309310 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);311 let collection = <CollectionHandle<T>>::try_get(collection_id)?;312313 <PalletCommon<T>>::toggle_allowlist(314 &collection,315 &sender,316 &address,317 false,318 )?;319320 Ok(())321 }322323 324 325 326 327 328 329 330 331 332 333 334 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]335 #[transactional]336 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult337 {338 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);339340 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;341 target_collection.check_is_owner(&sender)?;342343 target_collection.access = mode;344 target_collection.save()345 }346347 348 349 350 351 352 353 354 355 356 357 358 359 360 #[weight = <SelfWeightOf<T>>::set_mint_permission()]361 #[transactional]362 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult363 {364 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);365366 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;367 target_collection.check_is_owner(&sender)?;368369 target_collection.mint_mode = mint_permission;370 target_collection.save()371 }372373 374 375 376 377 378 379 380 381 382 383 384 #[weight = <SelfWeightOf<T>>::change_collection_owner()]385 #[transactional]386 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {387388 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);389390 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;391 target_collection.check_is_owner(&sender)?;392393 target_collection.owner = new_owner;394 target_collection.save()395 }396397 398 399 400 401 402 403 404 405 406 407 408 409 410 #[weight = <SelfWeightOf<T>>::add_collection_admin()]411 #[transactional]412 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {413 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);414415 let collection = <CollectionHandle<T>>::try_get(collection_id)?;416 collection.check_is_owner_or_admin(&sender)?;417418 <IsAdmin<T>>::insert((collection_id, new_admin_id.as_sub()), true);419 Ok(())420 }421422 423 424 425 426 427 428 429 430 431 432 433 434 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]435 #[transactional]436 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {437 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);438439 let collection = <CollectionHandle<T>>::try_get(collection_id)?;440 collection.check_is_owner_or_admin(&sender)?;441442 <IsAdmin<T>>::remove((collection_id, account_id.as_sub()));443 Ok(())444 }445446 447 448 449 450 451 452 453 454 455 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]456 #[transactional]457 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {458 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);459460 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;461 target_collection.check_is_owner_or_admin(&sender)?;462463 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);464 target_collection.save()465 }466467 468 469 470 471 472 473 474 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]475 #[transactional]476 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {477 let sender = ensure_signed(origin)?;478479 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;480 ensure!(481 target_collection.sponsorship.pending_sponsor() == Some(&sender),482 Error::<T>::ConfirmUnsetSponsorFail483 );484485 target_collection.sponsorship = SponsorshipState::Confirmed(sender);486 target_collection.save()487 }488489 490 491 492 493 494 495 496 497 498 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]499 #[transactional]500 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {501 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);502503 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;504 target_collection.check_is_owner(&sender)?;505506 target_collection.sponsorship = SponsorshipState::Disabled;507 target_collection.save()508 }509510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 #[weight = <CommonWeights<T>>::create_item()]529 #[transactional]530 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {531 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);532533 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data))534 }535536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 #[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]555 #[transactional]556 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {557 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);558 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);559560 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))561 }562563 564565 566 567 568 569 570 571 572 573 574 575 576 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]577 #[transactional]578 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {579 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);580 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;581 target_collection.check_is_owner(&sender)?;582583 584585 target_collection.transfers_enabled = value;586 target_collection.save()587 }588589 590 591 592 593 594 595 596 597 598 599 600 601 602 #[weight = <CommonWeights<T>>::burn_item()]603 #[transactional]604 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {605 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);606607 dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))608 }609610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 #[weight = <CommonWeights<T>>::burn_from()]627 #[transactional]628 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {629 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);630631 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))632 }633634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 #[weight = <CommonWeights<T>>::transfer()]658 #[transactional]659 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {660 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);661662 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value))663 }664665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 #[weight = <CommonWeights<T>>::approve()]681 #[transactional]682 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {683 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);684685 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))686 }687688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 #[weight = <CommonWeights<T>>::transfer_from()]708 #[transactional]709 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {710 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);711712 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value))713 }714715 716 717 718 719 720 721 722 723 724 725 726 727 #[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]728 #[transactional]729 pub fn set_variable_meta_data (730 origin,731 collection_id: CollectionId,732 item_id: TokenId,733 data: Vec<u8>734 ) -> DispatchResultWithPostInfo {735 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);736737 dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))738 }739740 741 742 743 744 745 746 747 748 749 750 751 #[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]752 #[transactional]753 pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {754 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);755 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;756757 ensure!(758 target_collection.meta_update_permission != MetaUpdatePermission::None,759 <CommonError<T>>::MetadataFlagFrozen,760 );761 target_collection.check_is_owner(&sender)?;762763 target_collection.meta_update_permission = value;764765 target_collection.save()766 }767768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 #[weight = <SelfWeightOf<T>>::set_schema_version()]783 #[transactional]784 pub fn set_schema_version(785 origin,786 collection_id: CollectionId,787 version: SchemaVersion788 ) -> DispatchResult {789 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);790 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;791 target_collection.check_is_owner_or_admin(&sender)?;792 target_collection.schema_version = version;793 target_collection.save()794 }795796 797 798 799 800 801 802 803 804 805 806 807 808 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]809 #[transactional]810 pub fn set_offchain_schema(811 origin,812 collection_id: CollectionId,813 schema: Vec<u8>814 ) -> DispatchResult {815 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);816 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;817 target_collection.check_is_owner_or_admin(&sender)?;818819 820 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");821822 target_collection.offchain_schema = schema;823 target_collection.save()824 }825826 827 828 829 830 831 832 833 834 835 836 837 838 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]839 #[transactional]840 pub fn set_const_on_chain_schema (841 origin,842 collection_id: CollectionId,843 schema: Vec<u8>844 ) -> DispatchResult {845 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);846 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;847 target_collection.check_is_owner_or_admin(&sender)?;848849 850 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");851852 target_collection.const_on_chain_schema = schema;853 target_collection.save()854 }855856 857 858 859 860 861 862 863 864 865 866 867 868 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]869 #[transactional]870 pub fn set_variable_on_chain_schema (871 origin,872 collection_id: CollectionId,873 schema: Vec<u8>874 ) -> DispatchResult {875 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);876 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;877 target_collection.check_is_owner_or_admin(&sender)?;878879 880 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");881882 target_collection.variable_on_chain_schema = schema;883 target_collection.save()884 }885886 #[weight = <SelfWeightOf<T>>::set_collection_limits()]887 #[transactional]888 pub fn set_collection_limits(889 origin,890 collection_id: CollectionId,891 new_limits: CollectionLimits<T::BlockNumber>,892 ) -> DispatchResult {893 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);894 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;895 target_collection.check_is_owner(&sender)?;896 let old_limits = &target_collection.limits;897898 899 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&900 new_limits.account_token_ownership_limit.unwrap_or(0) <= MAX_TOKEN_OWNERSHIP &&901 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,902 Error::<T>::CollectionLimitBoundsExceeded);903904 905 ensure!(old_limits.token_limit >= new_limits.token_limit, <CommonError<T>>::CollectionTokenLimitExceeded);906 ensure!(new_limits.token_limit > 0, <CommonError<T>>::CollectionTokenLimitExceeded);907908 ensure!(909 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&910 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),911 Error::<T>::OwnerPermissionsCantBeReverted,912 );913914 target_collection.limits = new_limits;915916 target_collection.save()917 }918 }919}920921922impl<T: Config> Pallet<T> {923 pub fn adminlist(collection: CollectionId) -> Vec<T::AccountId> {924 <IsAdmin<T>>::iter_prefix((collection,))925 .map(|(a, _)| a)926 .collect()927 }928 pub fn allowlist(collection: CollectionId) -> Vec<T::AccountId> {929 <Allowlist<T>>::iter_prefix((collection,))930 .map(|(a, _)| a)931 .collect()932 }933}