123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566#![recursion_limit = "1024"]67#![cfg_attr(not(feature = "std"), no_std)]68#![allow(69 clippy::too_many_arguments,70 clippy::unnecessary_mut_passed,71 clippy::unused_unit72)]7374extern crate alloc;7576use frame_support::pallet_prelude::*;77use frame_system::pallet_prelude::*;78pub use pallet::*;79pub mod eth;8081#[cfg(feature = "runtime-benchmarks")]82pub mod benchmarking;83pub mod weights;8485#[frame_support::pallet]86pub mod pallet {87 use frame_support::{88 dispatch::{DispatchErrorWithPostInfo, DispatchResult, PostDispatchInfo},89 ensure, fail,90 storage::Key,91 BoundedVec,92 };93 use frame_system::{ensure_root, ensure_signed};94 use pallet_common::{95 dispatch::{dispatch_tx, CollectionDispatch},96 CollectionHandle, CommonWeightInfo, Pallet as PalletCommon, RefungibleExtensionsWeightInfo,97 };98 use pallet_evm::account::CrossAccountId;99 use pallet_structure::weights::WeightInfo as StructureWeightInfo;100 use scale_info::TypeInfo;101 use sp_std::{vec, vec::Vec};102 use up_data_structs::{103 budget, CollectionId, CollectionLimits, CollectionMode, CollectionPermissions,104 CreateCollectionData, CreateItemData, CreateItemExData, Property, PropertyKey,105 PropertyKeyPermission, TokenId, COLLECTION_ADMINS_LIMIT, MAX_COLLECTION_DESCRIPTION_LENGTH,106 MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_PROPERTIES_SIZE, MAX_PROPERTIES_PER_ITEM,107 MAX_PROPERTY_KEY_LENGTH, MAX_PROPERTY_VALUE_LENGTH, MAX_TOKEN_PREFIX_LENGTH,108 MAX_TOKEN_PROPERTIES_SIZE,109 };110 use weights::WeightInfo;111112 use super::*;113114 115 #[pallet::error]116 pub enum Error<T> {117 118 CollectionDecimalPointLimitExceeded,119 120 EmptyArgument,121 122 RepartitionCalledOnNonRefungibleCollection,123 }124125 126 #[pallet::config]127 pub trait Config: frame_system::Config + pallet_common::Config + Sized + TypeInfo {128 129 type WeightInfo: WeightInfo;130131 132 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;133134 type StructureWeightInfo: StructureWeightInfo;135136 137 type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;138 }139140 #[pallet::pallet]141 pub struct Pallet<T>(_);142143 pub type SelfWeightOf<T> = <T as Config>::WeightInfo;144145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167168 169 170 #[pallet::storage]171 pub type ChainVersion<T> = StorageValue<_, u64, ValueQuery>;172 173174 175 176 177 #[pallet::storage]178 #[pallet::getter(fn create_item_busket)]179 pub type CreateItemBasket<T: Config> = StorageMap<180 Hasher = Blake2_128Concat,181 Key = (CollectionId, T::AccountId),182 Value = BlockNumberFor<T>,183 QueryKind = OptionQuery,184 >;185 186 #[pallet::storage]187 #[pallet::getter(fn nft_transfer_basket)]188 pub type NftTransferBasket<T: Config> = StorageDoubleMap<189 Hasher1 = Blake2_128Concat,190 Key1 = CollectionId,191 Hasher2 = Blake2_128Concat,192 Key2 = TokenId,193 Value = BlockNumberFor<T>,194 QueryKind = OptionQuery,195 >;196 197 #[pallet::storage]198 #[pallet::getter(fn fungible_transfer_basket)]199 pub type FungibleTransferBasket<T: Config> = StorageDoubleMap<200 Hasher1 = Blake2_128Concat,201 Key1 = CollectionId,202 Hasher2 = Twox64Concat,203 Key2 = T::AccountId,204 Value = BlockNumberFor<T>,205 QueryKind = OptionQuery,206 >;207 208 #[pallet::storage]209 #[pallet::getter(fn refungible_transfer_basket)]210 pub type ReFungibleTransferBasket<T: Config> = StorageNMap<211 Key = (212 Key<Blake2_128Concat, CollectionId>,213 Key<Blake2_128Concat, TokenId>,214 Key<Twox64Concat, T::AccountId>,215 ),216 Value = BlockNumberFor<T>,217 QueryKind = OptionQuery,218 >;219 220221 222 #[pallet::storage]223 #[pallet::getter(fn token_property_basket)]224 pub type TokenPropertyBasket<T: Config> = StorageDoubleMap<225 Hasher1 = Blake2_128Concat,226 Key1 = CollectionId,227 Hasher2 = Blake2_128Concat,228 Key2 = TokenId,229 Value = BlockNumberFor<T>,230 QueryKind = OptionQuery,231 >;232233 234 #[pallet::storage]235 #[pallet::getter(fn nft_approve_basket)]236 pub type NftApproveBasket<T: Config> = StorageDoubleMap<237 Hasher1 = Blake2_128Concat,238 Key1 = CollectionId,239 Hasher2 = Blake2_128Concat,240 Key2 = TokenId,241 Value = BlockNumberFor<T>,242 QueryKind = OptionQuery,243 >;244 245 #[pallet::storage]246 #[pallet::getter(fn fungible_approve_basket)]247 pub type FungibleApproveBasket<T: Config> = StorageDoubleMap<248 Hasher1 = Blake2_128Concat,249 Key1 = CollectionId,250 Hasher2 = Twox64Concat,251 Key2 = T::AccountId,252 Value = BlockNumberFor<T>,253 QueryKind = OptionQuery,254 >;255 256 #[pallet::storage]257 #[pallet::getter(fn refungible_approve_basket)]258 pub type RefungibleApproveBasket<T: Config> = StorageNMap<259 Key = (260 Key<Blake2_128Concat, CollectionId>,261 Key<Blake2_128Concat, TokenId>,262 Key<Twox64Concat, T::AccountId>,263 ),264 Value = BlockNumberFor<T>,265 QueryKind = OptionQuery,266 >;267268 #[pallet::extra_constants]269 impl<T: Config> Pallet<T> {270 271 fn nesting_budget() -> u32 {272 5273 }274275 276 fn max_collection_name_length() -> u32 {277 MAX_COLLECTION_NAME_LENGTH278 }279280 281 fn max_collection_description_length() -> u32 {282 MAX_COLLECTION_DESCRIPTION_LENGTH283 }284285 286 fn max_token_prefix_length() -> u32 {287 MAX_TOKEN_PREFIX_LENGTH288 }289290 291 fn collection_admins_limit() -> u32 {292 COLLECTION_ADMINS_LIMIT293 }294295 296 fn max_property_key_length() -> u32 {297 MAX_PROPERTY_KEY_LENGTH298 }299300 301 fn max_property_value_length() -> u32 {302 MAX_PROPERTY_VALUE_LENGTH303 }304305 306 fn max_properties_per_item() -> u32 {307 MAX_PROPERTIES_PER_ITEM308 }309310 311 fn max_collection_properties_size() -> u32 {312 MAX_COLLECTION_PROPERTIES_SIZE313 }314315 316 fn max_token_properties_size() -> u32 {317 MAX_TOKEN_PROPERTIES_SIZE318 }319320 321 fn nft_default_collection_limits() -> CollectionLimits {322 CollectionLimits::with_default_limits(CollectionMode::NFT)323 }324325 326 fn rft_default_collection_limits() -> CollectionLimits {327 CollectionLimits::with_default_limits(CollectionMode::ReFungible)328 }329330 331 fn ft_default_collection_limits() -> CollectionLimits {332 CollectionLimits::with_default_limits(CollectionMode::Fungible(0))333 }334 }335336 337 #[pallet::call]338 impl<T: Config> Pallet<T> {339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 #[pallet::call_index(0)]365 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]366 pub fn create_collection(367 origin: OriginFor<T>,368 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,369 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,370 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,371 mode: CollectionMode,372 ) -> DispatchResult {373 let data: CreateCollectionData<T::CrossAccountId> = CreateCollectionData {374 name: collection_name,375 description: collection_description,376 token_prefix,377 mode,378 ..Default::default()379 };380 Self::create_collection_ex(origin, data)381 }382383 384 385 386 387 388 389 390 391 392 393 394 #[pallet::call_index(1)]395 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]396 pub fn create_collection_ex(397 origin: OriginFor<T>,398 data: CreateCollectionData<T::CrossAccountId>,399 ) -> DispatchResult {400 let sender = ensure_signed(origin)?;401402 403 let sender = T::CrossAccountId::from_sub(sender);404 let _id = T::CollectionDispatch::create(sender.clone(), Some(sender), data)?;405406 Ok(())407 }408409 410 411 412 413 414 415 416 417 418 #[pallet::call_index(2)]419 #[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]420 pub fn destroy_collection(421 origin: OriginFor<T>,422 collection_id: CollectionId,423 ) -> DispatchResult {424 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);425426 Self::destroy_collection_internal(sender, collection_id)427 }428429 430 431 432 433 434 435 436 437 438 439 440 #[pallet::call_index(3)]441 #[pallet::weight(<SelfWeightOf<T>>::add_to_allow_list())]442 pub fn add_to_allow_list(443 origin: OriginFor<T>,444 collection_id: CollectionId,445 address: T::CrossAccountId,446 ) -> DispatchResult {447 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {448 fail!(<pallet_common::Error<T>>::UnsupportedOperation);449 }450451 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);452 let collection = <CollectionHandle<T>>::try_get(collection_id)?;453 collection.check_is_internal()?;454455 <PalletCommon<T>>::toggle_allowlist(&collection, &sender, &address, true)?;456457 Ok(())458 }459460 461 462 463 464 465 466 467 468 469 470 471 #[pallet::call_index(4)]472 #[pallet::weight(<SelfWeightOf<T>>::remove_from_allow_list())]473 pub fn remove_from_allow_list(474 origin: OriginFor<T>,475 collection_id: CollectionId,476 address: T::CrossAccountId,477 ) -> DispatchResult {478 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {479 fail!(<pallet_common::Error<T>>::UnsupportedOperation);480 }481482 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);483 let collection = <CollectionHandle<T>>::try_get(collection_id)?;484 collection.check_is_internal()?;485486 <PalletCommon<T>>::toggle_allowlist(&collection, &sender, &address, false)?;487488 Ok(())489 }490491 492 493 494 495 496 497 498 499 500 501 #[pallet::call_index(5)]502 #[pallet::weight(<SelfWeightOf<T>>::change_collection_owner())]503 pub fn change_collection_owner(504 origin: OriginFor<T>,505 collection_id: CollectionId,506 new_owner: T::AccountId,507 ) -> DispatchResult {508 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {509 fail!(<pallet_common::Error<T>>::UnsupportedOperation);510 }511 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);512 let new_owner = T::CrossAccountId::from_sub(new_owner);513 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;514 target_collection.change_owner(sender, new_owner)515 }516517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 #[pallet::call_index(6)]534 #[pallet::weight(<SelfWeightOf<T>>::add_collection_admin())]535 pub fn add_collection_admin(536 origin: OriginFor<T>,537 collection_id: CollectionId,538 new_admin_id: T::CrossAccountId,539 ) -> DispatchResult {540 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {541 fail!(<pallet_common::Error<T>>::UnsupportedOperation);542 }543 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);544 let collection = <CollectionHandle<T>>::try_get(collection_id)?;545 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)546 }547548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 #[pallet::call_index(7)]563 #[pallet::weight(<SelfWeightOf<T>>::remove_collection_admin())]564 pub fn remove_collection_admin(565 origin: OriginFor<T>,566 collection_id: CollectionId,567 account_id: T::CrossAccountId,568 ) -> DispatchResult {569 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {570 fail!(<pallet_common::Error<T>>::UnsupportedOperation);571 }572 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);573 let collection = <CollectionHandle<T>>::try_get(collection_id)?;574 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)575 }576577 578 579 580 581 582 583 584 585 586 587 588 589 590 #[pallet::call_index(8)]591 #[pallet::weight(<SelfWeightOf<T>>::set_collection_sponsor())]592 pub fn set_collection_sponsor(593 origin: OriginFor<T>,594 collection_id: CollectionId,595 new_sponsor: T::AccountId,596 ) -> DispatchResult {597 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {598 fail!(<pallet_common::Error<T>>::UnsupportedOperation);599 }600 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);601 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;602 target_collection.set_sponsor(&sender, new_sponsor.clone())603 }604605 606 607 608 609 610 611 612 613 614 615 616 617 618 #[pallet::call_index(9)]619 #[pallet::weight(<SelfWeightOf<T>>::confirm_sponsorship())]620 pub fn confirm_sponsorship(621 origin: OriginFor<T>,622 collection_id: CollectionId,623 ) -> DispatchResult {624 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {625 fail!(<pallet_common::Error<T>>::UnsupportedOperation);626 }627 let sender = ensure_signed(origin)?;628 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;629 target_collection.confirm_sponsorship(&sender)630 }631632 633 634 635 636 637 638 639 640 641 #[pallet::call_index(10)]642 #[pallet::weight(<SelfWeightOf<T>>::remove_collection_sponsor())]643 pub fn remove_collection_sponsor(644 origin: OriginFor<T>,645 collection_id: CollectionId,646 ) -> DispatchResult {647 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {648 fail!(<pallet_common::Error<T>>::UnsupportedOperation);649 }650 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);651 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;652 target_collection.remove_sponsor(&sender)653 }654655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 #[pallet::call_index(11)]674 #[pallet::weight(T::CommonWeightInfo::create_item(data) + <Pallet<T>>::nesting_budget_predispatch_weight())]675 pub fn create_item(676 origin: OriginFor<T>,677 collection_id: CollectionId,678 owner: T::CrossAccountId,679 data: CreateItemData,680 ) -> DispatchResultWithPostInfo {681 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);682 let budget = Self::structure_nesting_budget();683684 Self::refund_nesting_budget(685 dispatch_tx::<T, _>(collection_id, |d| {686 d.create_item(sender, owner, data, &budget)687 }),688 budget,689 )690 }691692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 #[pallet::call_index(12)]711 #[pallet::weight(T::CommonWeightInfo::create_multiple_items(items_data) + <Pallet<T>>::nesting_budget_predispatch_weight())]712 pub fn create_multiple_items(713 origin: OriginFor<T>,714 collection_id: CollectionId,715 owner: T::CrossAccountId,716 items_data: Vec<CreateItemData>,717 ) -> DispatchResultWithPostInfo {718 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);719 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);720 let budget = Self::structure_nesting_budget();721722 Self::refund_nesting_budget(723 dispatch_tx::<T, _>(collection_id, |d| {724 d.create_multiple_items(sender, owner, items_data, &budget)725 }),726 budget,727 )728 }729730 731 732 733 734 735 736 737 738 739 740 741 742 #[pallet::call_index(13)]743 #[pallet::weight(T::CommonWeightInfo::set_collection_properties(properties.len() as u32))]744 pub fn set_collection_properties(745 origin: OriginFor<T>,746 collection_id: CollectionId,747 properties: Vec<Property>,748 ) -> DispatchResultWithPostInfo {749 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);750751 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);752753 dispatch_tx::<T, _>(collection_id, |d| {754 d.set_collection_properties(sender, properties)755 })756 }757758 759 760 761 762 763 764 765 766 767 768 769 770 #[pallet::call_index(14)]771 #[pallet::weight(T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32))]772 pub fn delete_collection_properties(773 origin: OriginFor<T>,774 collection_id: CollectionId,775 property_keys: Vec<PropertyKey>,776 ) -> DispatchResultWithPostInfo {777 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);778779 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);780781 dispatch_tx::<T, _>(collection_id, |d| {782 d.delete_collection_properties(&sender, property_keys)783 })784 }785786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 #[pallet::call_index(15)]805 #[pallet::weight(T::CommonWeightInfo::set_token_properties(properties.len() as u32) + <Pallet<T>>::nesting_budget_predispatch_weight())]806 pub fn set_token_properties(807 origin: OriginFor<T>,808 collection_id: CollectionId,809 token_id: TokenId,810 properties: Vec<Property>,811 ) -> DispatchResultWithPostInfo {812 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);813814 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);815 let budget = Self::structure_nesting_budget();816817 Self::refund_nesting_budget(818 dispatch_tx::<T, _>(collection_id, |d| {819 d.set_token_properties(sender, token_id, properties, &budget)820 }),821 budget,822 )823 }824825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 #[pallet::call_index(16)]841 #[pallet::weight(T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32) + <Pallet<T>>::nesting_budget_predispatch_weight())]842 pub fn delete_token_properties(843 origin: OriginFor<T>,844 collection_id: CollectionId,845 token_id: TokenId,846 property_keys: Vec<PropertyKey>,847 ) -> DispatchResultWithPostInfo {848 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);849850 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);851 let budget = Self::structure_nesting_budget();852853 Self::refund_nesting_budget(854 dispatch_tx::<T, _>(collection_id, |d| {855 d.delete_token_properties(sender, token_id, property_keys, &budget)856 }),857 budget,858 )859 }860861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 #[pallet::call_index(17)]877 #[pallet::weight(T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32))]878 pub fn set_token_property_permissions(879 origin: OriginFor<T>,880 collection_id: CollectionId,881 property_permissions: Vec<PropertyKeyPermission>,882 ) -> DispatchResultWithPostInfo {883 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);884885 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);886887 dispatch_tx::<T, _>(collection_id, |d| {888 d.set_token_property_permissions(&sender, property_permissions)889 })890 }891892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 #[pallet::call_index(18)]908 #[pallet::weight(T::CommonWeightInfo::create_multiple_items_ex(data) + <Pallet<T>>::nesting_budget_predispatch_weight())]909 pub fn create_multiple_items_ex(910 origin: OriginFor<T>,911 collection_id: CollectionId,912 data: CreateItemExData<T::CrossAccountId>,913 ) -> DispatchResultWithPostInfo {914 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);915 let budget = Self::structure_nesting_budget();916917 Self::refund_nesting_budget(918 dispatch_tx::<T, _>(collection_id, |d| {919 d.create_multiple_items_ex(sender, data, &budget)920 }),921 budget,922 )923 }924925 926 927 928 929 930 931 932 933 934 935 #[pallet::call_index(19)]936 #[pallet::weight(<SelfWeightOf<T>>::set_transfers_enabled_flag())]937 pub fn set_transfers_enabled_flag(938 origin: OriginFor<T>,939 collection_id: CollectionId,940 value: bool,941 ) -> DispatchResult {942 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {943 fail!(<pallet_common::Error<T>>::UnsupportedOperation);944 }945 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);946 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;947 target_collection.check_is_internal()?;948 target_collection.check_is_owner(&sender)?;949950 951952 target_collection.limits.transfers_enabled = Some(value);953 target_collection.save()954 }955956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 #[pallet::call_index(20)]973 #[pallet::weight(T::CommonWeightInfo::burn_item())]974 pub fn burn_item(975 origin: OriginFor<T>,976 collection_id: CollectionId,977 item_id: TokenId,978 value: u128,979 ) -> DispatchResultWithPostInfo {980 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);981982 let post_info =983 dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;984 if value == 1 {985 <NftTransferBasket<T>>::remove(collection_id, item_id);986 <NftApproveBasket<T>>::remove(collection_id, item_id);987 }988 989 990 991 Ok(post_info)992 }993994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 #[pallet::call_index(21)]1018 #[pallet::weight(T::CommonWeightInfo::burn_from() + <Pallet<T>>::nesting_budget_predispatch_weight())]1019 pub fn burn_from(1020 origin: OriginFor<T>,1021 collection_id: CollectionId,1022 from: T::CrossAccountId,1023 item_id: TokenId,1024 value: u128,1025 ) -> DispatchResultWithPostInfo {1026 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1027 let budget = Self::structure_nesting_budget();10281029 Self::refund_nesting_budget(1030 dispatch_tx::<T, _>(collection_id, |d| {1031 d.burn_from(sender, from, item_id, value, &budget)1032 }),1033 budget,1034 )1035 }10361037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 #[pallet::call_index(22)]1059 #[pallet::weight(T::CommonWeightInfo::transfer() + <Pallet<T>>::nesting_budget_predispatch_weight())]1060 pub fn transfer(1061 origin: OriginFor<T>,1062 recipient: T::CrossAccountId,1063 collection_id: CollectionId,1064 item_id: TokenId,1065 value: u128,1066 ) -> DispatchResultWithPostInfo {1067 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1068 let budget = Self::structure_nesting_budget();10691070 Self::refund_nesting_budget(1071 dispatch_tx::<T, _>(collection_id, |d| {1072 d.transfer(sender, recipient, item_id, value, &budget)1073 }),1074 budget,1075 )1076 }10771078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 #[pallet::call_index(23)]1094 #[pallet::weight(T::CommonWeightInfo::approve())]1095 pub fn approve(1096 origin: OriginFor<T>,1097 spender: T::CrossAccountId,1098 collection_id: CollectionId,1099 item_id: TokenId,1100 amount: u128,1101 ) -> DispatchResultWithPostInfo {1102 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11031104 dispatch_tx::<T, _>(collection_id, |d| {1105 d.approve(sender, spender, item_id, amount)1106 })1107 }11081109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 #[pallet::call_index(24)]1126 #[pallet::weight(T::CommonWeightInfo::approve_from())]1127 pub fn approve_from(1128 origin: OriginFor<T>,1129 from: T::CrossAccountId,1130 to: T::CrossAccountId,1131 collection_id: CollectionId,1132 item_id: TokenId,1133 amount: u128,1134 ) -> DispatchResultWithPostInfo {1135 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11361137 dispatch_tx::<T, _>(collection_id, |d| {1138 d.approve_from(sender, from, to, item_id, amount)1139 })1140 }11411142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 #[pallet::call_index(25)]1167 #[pallet::weight(T::CommonWeightInfo::transfer_from() + <Pallet<T>>::nesting_budget_predispatch_weight())]1168 pub fn transfer_from(1169 origin: OriginFor<T>,1170 from: T::CrossAccountId,1171 recipient: T::CrossAccountId,1172 collection_id: CollectionId,1173 item_id: TokenId,1174 value: u128,1175 ) -> DispatchResultWithPostInfo {1176 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1177 let budget = Self::structure_nesting_budget();11781179 Self::refund_nesting_budget(1180 dispatch_tx::<T, _>(collection_id, |d| {1181 d.transfer_from(sender, from, recipient, item_id, value, &budget)1182 }),1183 budget,1184 )1185 }11861187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 #[pallet::call_index(26)]1200 #[pallet::weight(<SelfWeightOf<T>>::set_collection_limits())]1201 pub fn set_collection_limits(1202 origin: OriginFor<T>,1203 collection_id: CollectionId,1204 new_limit: CollectionLimits,1205 ) -> DispatchResult {1206 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1207 fail!(<pallet_common::Error<T>>::UnsupportedOperation);1208 }1209 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1210 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1211 <PalletCommon<T>>::update_limits(&sender, &mut target_collection, new_limit)1212 }12131214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 #[pallet::call_index(27)]1227 #[pallet::weight(<SelfWeightOf<T>>::set_collection_limits())]1228 pub fn set_collection_permissions(1229 origin: OriginFor<T>,1230 collection_id: CollectionId,1231 new_permission: CollectionPermissions,1232 ) -> DispatchResult {1233 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1234 fail!(<pallet_common::Error<T>>::UnsupportedOperation);1235 }1236 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1237 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1238 <PalletCommon<T>>::update_permissions(&sender, &mut target_collection, new_permission)1239 }12401241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 #[pallet::call_index(28)]1253 #[pallet::weight(T::RefungibleExtensionsWeightInfo::repartition())]1254 pub fn repartition(1255 origin: OriginFor<T>,1256 collection_id: CollectionId,1257 token_id: TokenId,1258 amount: u128,1259 ) -> DispatchResultWithPostInfo {1260 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1261 dispatch_tx::<T, _>(collection_id, |d| {1262 if let Some(refungible_extensions) = d.refungible_extensions() {1263 refungible_extensions.repartition(&sender, token_id, amount)1264 } else {1265 fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1266 }1267 })1268 }12691270 1271 1272 1273 1274 1275 1276 1277 1278 1279 #[pallet::call_index(29)]1280 #[pallet::weight(T::CommonWeightInfo::set_allowance_for_all())]1281 pub fn set_allowance_for_all(1282 origin: OriginFor<T>,1283 collection_id: CollectionId,1284 operator: T::CrossAccountId,1285 approve: bool,1286 ) -> DispatchResultWithPostInfo {1287 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1288 dispatch_tx::<T, _>(collection_id, |d| {1289 d.set_allowance_for_all(sender, operator, approve)1290 })1291 }12921293 1294 1295 1296 1297 1298 #[pallet::call_index(30)]1299 #[pallet::weight(<SelfWeightOf<T>>::force_repair_collection())]1300 pub fn force_repair_collection(1301 origin: OriginFor<T>,1302 collection_id: CollectionId,1303 ) -> DispatchResult {1304 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1305 fail!(<pallet_common::Error<T>>::UnsupportedOperation);1306 }1307 ensure_root(origin)?;1308 <PalletCommon<T>>::repair_collection(collection_id)1309 }13101311 1312 1313 1314 1315 1316 1317 #[pallet::call_index(31)]1318 #[pallet::weight(T::CommonWeightInfo::force_repair_item())]1319 pub fn force_repair_item(1320 origin: OriginFor<T>,1321 collection_id: CollectionId,1322 item_id: TokenId,1323 ) -> DispatchResultWithPostInfo {1324 ensure_root(origin)?;1325 dispatch_tx::<T, _>(collection_id, |d| d.repair_item(item_id))1326 }1327 }13281329 impl<T: Config> Pallet<T> {1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 pub fn force_set_sponsor(1340 sponsor: T::AccountId,1341 collection_id: CollectionId,1342 ) -> DispatchResult {1343 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1344 target_collection.force_set_sponsor(sponsor)1345 }13461347 1348 1349 1350 1351 1352 1353 1354 1355 pub fn force_remove_collection_sponsor(collection_id: CollectionId) -> DispatchResult {1356 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1357 target_collection.force_remove_sponsor()1358 }13591360 #[inline(always)]1361 pub(crate) fn destroy_collection_internal(1362 sender: T::CrossAccountId,1363 collection_id: CollectionId,1364 ) -> DispatchResult {1365 T::CollectionDispatch::destroy(sender, collection_id)?;13661367 1368 13691370 let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1371 let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1372 let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);13731374 let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1375 let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1376 let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);13771378 Ok(())1379 }13801381 fn structure_nesting_budget() -> budget::Value {1382 budget::Value::new(Self::nesting_budget())1383 }13841385 fn nesting_budget_predispatch_weight() -> Weight {1386 T::StructureWeightInfo::find_parent().saturating_mul(Self::nesting_budget() as u64)1387 }13881389 pub fn refund_nesting_budget(1390 mut result: DispatchResultWithPostInfo,1391 budget: budget::Value,1392 ) -> DispatchResultWithPostInfo {1393 let refund_amount = budget.refund_amount();1394 let consumed = Self::nesting_budget() - refund_amount;13951396 match &mut result {1397 Ok(PostDispatchInfo {1398 actual_weight: Some(weight),1399 ..1400 })1401 | Err(DispatchErrorWithPostInfo {1402 post_info: PostDispatchInfo {1403 actual_weight: Some(weight),1404 ..1405 },1406 ..1407 }) => {1408 *weight += T::StructureWeightInfo::find_parent().saturating_mul(consumed as u64)1409 }1410 _ => {}1411 }14121413 result1414 }1415 }1416}