1use crate::{2 Config, Call, CollectionById, CreateItemBasket, VariableMetaDataBasket,3 ReFungibleTransferBasket, FungibleTransferBasket, NftTransferBasket, CreateItemData,4 CollectionMode,5};6use core::marker::PhantomData;7use up_sponsorship::SponsorshipHandler;8use frame_support::{9 traits::{IsSubType},10 storage::{StorageMap, StorageDoubleMap},11};12use nft_data_structs::{13 TokenId, CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,14 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,15};1617pub struct NftSponsorshipHandler<T>(PhantomData<T>);18impl<T: Config> NftSponsorshipHandler<T> {19 pub fn withdraw_create_item(20 who: &T::AccountId,21 collection_id: &CollectionId,22 _properties: &CreateItemData,23 ) -> Option<T::AccountId> {24 let collection = CollectionById::<T>::get(collection_id)?;2526 27 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;2829 let limit = collection.limits.sponsor_transfer_timeout;30 if CreateItemBasket::<T>::contains_key((collection_id, &who)) {31 let last_tx_block = CreateItemBasket::<T>::get((collection_id, &who));32 let limit_time = last_tx_block + limit.into();33 if block_number <= limit_time {34 return None;35 }36 }37 CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);3839 40 if collection.limits.sponsored_data_size >= (_properties.data_size() as u32) {41 collection.sponsorship.sponsor().cloned()42 } else {43 None44 }45 }4647 pub fn withdraw_transfer(48 who: &T::AccountId,49 collection_id: &CollectionId,50 item_id: &TokenId,51 ) -> Option<T::AccountId> {52 let collection = CollectionById::<T>::get(collection_id)?;5354 let mut sponsor_transfer = false;55 if collection.sponsorship.confirmed() {56 let collection_limits = collection.limits.clone();57 let collection_mode = collection.mode.clone();5859 60 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;61 sponsor_transfer = match collection_mode {62 CollectionMode::NFT => {63 64 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {65 collection_limits.sponsor_transfer_timeout66 } else {67 NFT_SPONSOR_TRANSFER_TIMEOUT68 };6970 let mut sponsored = true;71 if NftTransferBasket::<T>::contains_key(collection_id, item_id) {72 let last_tx_block = NftTransferBasket::<T>::get(collection_id, item_id);73 let limit_time = last_tx_block + limit.into();74 if block_number <= limit_time {75 sponsored = false;76 }77 }78 if sponsored {79 NftTransferBasket::<T>::insert(collection_id, item_id, block_number);80 }8182 sponsored83 }84 CollectionMode::Fungible(_) => {85 86 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {87 collection_limits.sponsor_transfer_timeout88 } else {89 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT90 };9192 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;93 let mut sponsored = true;94 if FungibleTransferBasket::<T>::contains_key(collection_id, who) {95 let last_tx_block = FungibleTransferBasket::<T>::get(collection_id, who);96 let limit_time = last_tx_block + limit.into();97 if block_number <= limit_time {98 sponsored = false;99 }100 }101 if sponsored {102 FungibleTransferBasket::<T>::insert(collection_id, who, block_number);103 }104105 sponsored106 }107 CollectionMode::ReFungible => {108 109 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {110 collection_limits.sponsor_transfer_timeout111 } else {112 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT113 };114115 let mut sponsored = true;116 if ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {117 let last_tx_block =118 ReFungibleTransferBasket::<T>::get(collection_id, item_id);119 let limit_time = last_tx_block + limit.into();120 if block_number <= limit_time {121 sponsored = false;122 }123 }124 if sponsored {125 ReFungibleTransferBasket::<T>::insert(collection_id, item_id, block_number);126 }127128 sponsored129 }130 _ => false,131 };132 }133134 if !sponsor_transfer {135 None136 } else {137 collection.sponsorship.sponsor().cloned()138 }139 }140141 pub fn withdraw_set_variable_meta_data(142 collection_id: &CollectionId,143 item_id: &TokenId,144 data: &[u8],145 ) -> Option<T::AccountId> {146 let mut sponsor_metadata_changes = false;147148 let collection = CollectionById::<T>::get(collection_id)?;149150 if collection.sponsorship.confirmed() &&151 152 153 !matches!(collection.mode, CollectionMode::Fungible(_)) &&154 data.len() <= collection.limits.sponsored_data_size as usize155 {156 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {157 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;158159 if VariableMetaDataBasket::<T>::get(collection_id, item_id)160 .map(|last_block| block_number - last_block > rate_limit)161 .unwrap_or(true)162 {163 sponsor_metadata_changes = true;164 VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);165 }166 }167 }168169 if !sponsor_metadata_changes {170 None171 } else {172 collection.sponsorship.sponsor().cloned()173 }174 }175}176177impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>178where179 T: Config,180 C: IsSubType<Call<T>>,181{182 fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {183 match IsSubType::<Call<T>>::is_sub_type(call)? {184 Call::create_item(collection_id, _owner, properties) => {185 Self::withdraw_create_item(who, collection_id, properties)186 }187 Call::transfer(_new_owner, collection_id, item_id, _value) => {188 Self::withdraw_transfer(who, collection_id, item_id)189 }190 Call::set_variable_meta_data(collection_id, item_id, data) => {191 Self::withdraw_set_variable_meta_data(collection_id, item_id, data)192 }193 _ => None,194 }195 }196}