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 frame_system::{self as system, ensure_signed};35use sp_runtime::{sp_std::prelude::Vec};36use nft_data_structs::{37 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, CUSTOM_DATA_LIMIT,38 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,39 OFFCHAIN_SCHEMA_LIMIT, AccessMode, Collection, CreateItemData, CollectionLimits, CollectionId,40 CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,41};42use pallet_common::{43 account::CrossAccountId, CollectionHandle, IsAdmin, Pallet as PalletCommon,44 Error as CommonError, CommonWeightInfo, Allowlist,45};46use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};47use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};48use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};4950#[cfg(test)]51mod mock;5253#[cfg(test)]54mod tests;5556mod eth;57mod sponsorship;58pub use sponsorship::NftSponsorshipHandler;59pub use eth::sponsoring::NftEthSponsorshipHandler;6061pub use eth::NftErcSupport;6263pub mod common;64use common::CommonWeights;65pub mod dispatch;66use dispatch::dispatch_call;6768#[cfg(feature = "runtime-benchmarks")]69mod benchmarking;70pub mod weights;71use weights::WeightInfo;7273decl_error! {74 75 pub enum Error for Module<T: Config> {76 77 CollectionDecimalPointLimitExceeded,78 79 ConfirmUnsetSponsorFail,80 81 EmptyArgument,82 83 CollectionLimitBoundsExceeded,84 85 OwnerPermissionsCantBeReverted,86 }87}88pub trait Config:89 system::Config90 + pallet_evm_coder_substrate::Config91 + pallet_common::Config92 + pallet_nonfungible::Config93 + pallet_refungible::Config94 + pallet_fungible::Config95 + Sized96{97 98 type WeightInfo: WeightInfo;99}100101type SelfWeightOf<T> = <T as Config>::WeightInfo;102103trait WeightInfoHelpers: WeightInfo {104 fn transfer() -> Weight {105 Self::transfer_nft()106 .max(Self::transfer_fungible())107 .max(Self::transfer_refungible())108 }109 fn transfer_from() -> Weight {110 Self::transfer_from_nft()111 .max(Self::transfer_from_fungible())112 .max(Self::transfer_from_refungible())113 }114 fn approve() -> Weight {115 116 Self::approve_nft()117 }118 fn set_variable_meta_data(data: u32) -> Weight {119 120 Self::set_variable_meta_data_nft(data)121 }122 fn create_item(data: u32) -> Weight {123 Self::create_item_nft(data)124 .max(Self::create_item_fungible())125 .max(Self::create_item_refungible(data))126 }127 fn create_multiple_items(amount: u32) -> Weight {128 Self::create_multiple_items_nft(amount)129 .max(Self::create_multiple_items_fungible(amount))130 .max(Self::create_multiple_items_refungible(amount))131 }132}133impl<T: WeightInfo> WeightInfoHelpers for T {}134135136137138139140141142143144145146147148149150151152153154155156157decl_storage! {158 trait Store for Module<T: Config> as Nft {159160 161 162 ChainVersion: u64;163 164165 166 167 168 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;169 170 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;171 172 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;173 174 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;175 176177 178 179 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;180 }181}182183decl_module! {184 pub struct Module<T: Config> for enum Call185 where186 origin: T::Origin187 {188 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;189 type Error = Error<T>;190191 fn on_initialize(_now: T::BlockNumber) -> Weight {192 0193 }194195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 #[weight = <SelfWeightOf<T>>::create_collection()]212 #[transactional]213 pub fn create_collection(origin,214 collection_name: Vec<u16>,215 collection_description: Vec<u16>,216 token_prefix: Vec<u8>,217 mode: CollectionMode) -> DispatchResult {218219 220 let who = ensure_signed(origin)?;221222 let limits = CollectionLimits::<T::BlockNumber> {223 sponsored_data_size: CUSTOM_DATA_LIMIT,224 ..Default::default()225 };226227 228 let new_collection = Collection::<T> {229 owner: who.clone(),230 name: collection_name,231 mode: mode.clone(),232 mint_mode: false,233 access: AccessMode::Normal,234 description: collection_description,235 token_prefix,236 offchain_schema: Vec::new(),237 schema_version: SchemaVersion::ImageURL,238 sponsorship: SponsorshipState::Disabled,239 variable_on_chain_schema: Vec::new(),240 const_on_chain_schema: Vec::new(),241 limits,242 transfers_enabled: true,243 meta_update_permission: Default::default(),244 };245246 let _id = match mode {247 CollectionMode::NFT => {PalletNonfungible::init_collection(new_collection)?},248 CollectionMode::Fungible(decimal_points) => {249 250 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);251 PalletFungible::init_collection(new_collection)?252 }253 CollectionMode::ReFungible => {254 PalletRefungible::init_collection(new_collection)?255 }256 };257258 Ok(())259 }260261 262 263 264 265 266 267 268 269 270 #[weight = <SelfWeightOf<T>>::destroy_collection()]271 #[transactional]272 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {273 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);274275 let collection = <CollectionHandle<T>>::try_get(collection_id)?;276 collection.check_is_owner(&sender)?;277278 279280 match collection.mode {281 CollectionMode::ReFungible => PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?,282 CollectionMode::Fungible(_) => PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?,283 CollectionMode::NFT => PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?,284 }285286 <NftTransferBasket<T>>::remove_prefix(collection_id, None);287 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);288 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);289290 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);291292 Ok(())293 }294295 296 297 298 299 300 301 302 303 304 305 306 307 #[weight = <SelfWeightOf<T>>::add_to_white_list()]308 #[transactional]309 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{310311 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);312 let collection = <CollectionHandle<T>>::try_get(collection_id)?;313314 <PalletCommon<T>>::toggle_allowlist(315 &collection,316 &sender,317 &address,318 true,319 )?;320321 Ok(())322 }323324 325 326 327 328 329 330 331 332 333 334 335 336 #[weight = <SelfWeightOf<T>>::remove_from_white_list()]337 #[transactional]338 pub fn remove_from_white_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 false,348 )?;349350 Ok(())351 }352353 354 355 356 357 358 359 360 361 362 363 364 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]365 #[transactional]366 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult367 {368 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);369370 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;371 target_collection.check_is_owner(&sender)?;372373 target_collection.access = mode;374 target_collection.save()375 }376377 378 379 380 381 382 383 384 385 386 387 388 389 390 #[weight = <SelfWeightOf<T>>::set_mint_permission()]391 #[transactional]392 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult393 {394 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);395396 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;397 target_collection.check_is_owner(&sender)?;398399 target_collection.mint_mode = mint_permission;400 target_collection.save()401 }402403 404 405 406 407 408 409 410 411 412 413 414 #[weight = <SelfWeightOf<T>>::change_collection_owner()]415 #[transactional]416 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {417418 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);419420 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;421 target_collection.check_is_owner(&sender)?;422423 target_collection.owner = new_owner;424 target_collection.save()425 }426427 428 429 430 431 432 433 434 435 436 437 438 439 440 #[weight = <SelfWeightOf<T>>::add_collection_admin()]441 #[transactional]442 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {443 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);444445 let collection = <CollectionHandle<T>>::try_get(collection_id)?;446 collection.check_is_owner_or_admin(&sender)?;447448 <IsAdmin<T>>::insert((collection_id, new_admin_id.as_sub()), true);449 Ok(())450 }451452 453 454 455 456 457 458 459 460 461 462 463 464 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]465 #[transactional]466 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {467 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);468469 let collection = <CollectionHandle<T>>::try_get(collection_id)?;470 collection.check_is_owner_or_admin(&sender)?;471472 <IsAdmin<T>>::remove((collection_id, account_id.as_sub()));473 Ok(())474 }475476 477 478 479 480 481 482 483 484 485 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]486 #[transactional]487 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {488 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);489490 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;491 target_collection.check_is_owner_or_admin(&sender)?;492493 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);494 target_collection.save()495 }496497 498 499 500 501 502 503 504 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]505 #[transactional]506 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {507 let sender = ensure_signed(origin)?;508509 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;510 ensure!(511 target_collection.sponsorship.pending_sponsor() == Some(&sender),512 Error::<T>::ConfirmUnsetSponsorFail513 );514515 target_collection.sponsorship = SponsorshipState::Confirmed(sender);516 target_collection.save()517 }518519 520 521 522 523 524 525 526 527 528 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]529 #[transactional]530 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {531 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);532533 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;534 target_collection.check_is_owner(&sender)?;535536 target_collection.sponsorship = SponsorshipState::Disabled;537 target_collection.save()538 }539540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 #[weight = <CommonWeights<T>>::create_item()]559 #[transactional]560 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {561 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);562563 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data))564 }565566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 #[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]585 #[transactional]586 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {587 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);588 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);589590 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))591 }592593 594595 596 597 598 599 600 601 602 603 604 605 606 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]607 #[transactional]608 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {609 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);610 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;611 target_collection.check_is_owner(&sender)?;612613 614615 target_collection.transfers_enabled = value;616 target_collection.save()617 }618619 620 621 622 623 624 625 626 627 628 629 630 631 632 #[weight = <CommonWeights<T>>::burn_item()]633 #[transactional]634 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {635 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);636637 dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))638 }639640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 #[weight = <CommonWeights<T>>::transfer()]664 #[transactional]665 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {666 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);667668 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value))669 }670671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 #[weight = <CommonWeights<T>>::approve()]687 #[transactional]688 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {689 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);690691 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))692 }693694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 #[weight = <CommonWeights<T>>::transfer_from()]714 #[transactional]715 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {716 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);717718 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value))719 }720721 722 723 724 725 726 727 728 729 730 731 732 733 #[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]734 #[transactional]735 pub fn set_variable_meta_data (736 origin,737 collection_id: CollectionId,738 item_id: TokenId,739 data: Vec<u8>740 ) -> DispatchResultWithPostInfo {741 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);742743 dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))744 }745746 747 748 749 750 751 752 753 754 755 756 757 #[weight = <SelfWeightOf<T>>::set_variable_meta_data(0)]758 #[transactional]759 pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {760 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);761 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;762763 ensure!(764 target_collection.meta_update_permission != MetaUpdatePermission::None,765 <CommonError<T>>::MetadataFlagFrozen,766 );767 target_collection.check_is_owner(&sender)?;768769 target_collection.meta_update_permission = value;770771 target_collection.save()772 }773774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 #[weight = <SelfWeightOf<T>>::set_schema_version()]789 #[transactional]790 pub fn set_schema_version(791 origin,792 collection_id: CollectionId,793 version: SchemaVersion794 ) -> DispatchResult {795 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);796 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;797 target_collection.check_is_owner_or_admin(&sender)?;798 target_collection.schema_version = version;799 target_collection.save()800 }801802 803 804 805 806 807 808 809 810 811 812 813 814 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]815 #[transactional]816 pub fn set_offchain_schema(817 origin,818 collection_id: CollectionId,819 schema: Vec<u8>820 ) -> DispatchResult {821 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);822 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;823 target_collection.check_is_owner_or_admin(&sender)?;824825 826 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");827828 target_collection.offchain_schema = schema;829 target_collection.save()830 }831832 833 834 835 836 837 838 839 840 841 842 843 844 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]845 #[transactional]846 pub fn set_const_on_chain_schema (847 origin,848 collection_id: CollectionId,849 schema: Vec<u8>850 ) -> DispatchResult {851 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);852 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;853 target_collection.check_is_owner_or_admin(&sender)?;854855 856 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");857858 target_collection.const_on_chain_schema = schema;859 target_collection.save()860 }861862 863 864 865 866 867 868 869 870 871 872 873 874 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]875 #[transactional]876 pub fn set_variable_on_chain_schema (877 origin,878 collection_id: CollectionId,879 schema: Vec<u8>880 ) -> DispatchResult {881 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);882 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;883 target_collection.check_is_owner_or_admin(&sender)?;884885 886 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");887888 target_collection.variable_on_chain_schema = schema;889 target_collection.save()890 }891892 #[weight = <SelfWeightOf<T>>::set_collection_limits()]893 #[transactional]894 pub fn set_collection_limits(895 origin,896 collection_id: CollectionId,897 new_limits: CollectionLimits<T::BlockNumber>,898 ) -> DispatchResult {899 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);900 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;901 target_collection.check_is_owner(&sender)?;902 let old_limits = &target_collection.limits;903904 905 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&906 new_limits.account_token_ownership_limit.unwrap_or(0) <= MAX_TOKEN_OWNERSHIP &&907 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,908 Error::<T>::CollectionLimitBoundsExceeded);909910 911 ensure!(old_limits.token_limit >= new_limits.token_limit, <CommonError<T>>::CollectionTokenLimitExceeded);912 ensure!(new_limits.token_limit > 0, <CommonError<T>>::CollectionTokenLimitExceeded);913914 ensure!(915 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&916 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),917 Error::<T>::OwnerPermissionsCantBeReverted,918 );919920 target_collection.limits = new_limits;921922 target_collection.save()923 }924 }925}926927928impl<T: Config> Pallet<T> {929 pub fn adminlist(collection: CollectionId) -> Vec<T::AccountId> {930 <IsAdmin<T>>::iter_prefix((collection,))931 .map(|(a, _)| a)932 .collect()933 }934 pub fn allowlist(collection: CollectionId) -> Vec<T::AccountId> {935 <Allowlist<T>>::iter_prefix((collection,))936 .map(|(a, _)| a)937 .collect()938 }939}