1234567891011121314151617use core::marker::PhantomData;18use up_sponsorship::SponsorshipHandler;19use frame_support::{20 traits::{IsSubType},21};22use up_data_structs::{23 CollectionId, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, NFT_SPONSOR_TRANSFER_TIMEOUT,24 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, TokenId, CollectionMode, CreateItemData,25};26use sp_runtime::traits::Saturating;27use pallet_common::{CollectionHandle};28use pallet_evm::account::CrossAccountId;29use pallet_unique::{30 Call as UniqueCall, Config as UniqueConfig, FungibleApproveBasket, RefungibleApproveBasket,31 NftApproveBasket, CreateItemBasket, ReFungibleTransferBasket, FungibleTransferBasket,32 NftTransferBasket, TokenPropertyBasket,33};34use pallet_fungible::Config as FungibleConfig;35use pallet_nonfungible::Config as NonfungibleConfig;36use pallet_refungible::Config as RefungibleConfig;3738pub trait Config: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig {}39impl<T> Config for T where T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig {}404142pub fn withdraw_set_existing_token_property<T: Config>(43 collection: &CollectionHandle<T>,44 who: &T::CrossAccountId,45 item_id: &TokenId,46 data_size: usize,47) -> Option<()> {48 49 match collection.mode {50 CollectionMode::NFT => {51 let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;52 if !owner.conv_eq(who) {53 return None;54 }55 }56 CollectionMode::Fungible(_) => {57 58 return None;59 }60 CollectionMode::ReFungible => {61 if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {62 return None;63 }64 }65 }6667 withdraw_set_token_property(collection, item_id, data_size)68}6970pub fn withdraw_set_token_property<T: Config>(71 collection: &CollectionHandle<T>,72 item_id: &TokenId,73 data_size: usize,74) -> Option<()> {75 if data_size == 0 {76 return Some(());77 }78 if data_size > collection.limits.sponsored_data_size() as usize {79 return None;80 }8182 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;83 let limit = collection.limits.sponsored_data_rate_limit()?;8485 if let Some(last_tx_block) = TokenPropertyBasket::<T>::get(collection.id, item_id) {86 let timeout = last_tx_block + limit.into();87 if block_number < timeout {88 return None;89 }90 }9192 <TokenPropertyBasket<T>>::insert(collection.id, item_id, block_number);9394 Some(())95}9697pub fn withdraw_transfer<T: Config>(98 collection: &CollectionHandle<T>,99 who: &T::CrossAccountId,100 item_id: &TokenId,101) -> Option<()> {102 103 match collection.mode {104 CollectionMode::NFT => {105 let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;106 if !owner.conv_eq(who) {107 return None;108 }109 }110 CollectionMode::Fungible(_) => {111 if item_id != &TokenId::default() {112 return None;113 }114 if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {115 return None;116 }117 }118 CollectionMode::ReFungible => {119 if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {120 return None;121 }122 }123 }124125 126 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;127 let limit = collection128 .limits129 .sponsor_transfer_timeout(match collection.mode {130 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,131 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,132 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,133 });134135 let last_tx_block = match collection.mode {136 CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),137 CollectionMode::Fungible(_) => {138 <FungibleTransferBasket<T>>::get(collection.id, who.as_sub())139 }140 CollectionMode::ReFungible => {141 <ReFungibleTransferBasket<T>>::get((collection.id, item_id, who.as_sub()))142 }143 };144145 if let Some(last_tx_block) = last_tx_block {146 let timeout = last_tx_block + limit.into();147 if block_number < timeout {148 return None;149 }150 }151152 match collection.mode {153 CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),154 CollectionMode::Fungible(_) => {155 <FungibleTransferBasket<T>>::insert(collection.id, who.as_sub(), block_number)156 }157 CollectionMode::ReFungible => <ReFungibleTransferBasket<T>>::insert(158 (collection.id, item_id, who.as_sub()),159 block_number,160 ),161 };162163 Some(())164}165166pub fn withdraw_create_item<T: Config>(167 collection: &CollectionHandle<T>,168 who: &T::CrossAccountId,169 properties: &CreateItemData,170) -> Option<()> {171 172 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;173 let limit = collection174 .limits175 .sponsor_transfer_timeout(match properties {176 CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,177 CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,178 CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,179 });180181 if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, who.as_sub())) {182 let timeout = last_tx_block + limit.into();183 if block_number < timeout {184 return None;185 }186 }187 CreateItemBasket::<T>::insert((collection.id, who.as_sub()), block_number);188189 Some(())190}191192pub fn withdraw_approve<T: Config>(193 collection: &CollectionHandle<T>,194 who: &T::AccountId,195 item_id: &TokenId,196) -> Option<()> {197 198 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;199 let limit = collection.limits.sponsor_approve_timeout();200201 let last_tx_block = match collection.mode {202 CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),203 CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),204 CollectionMode::ReFungible => {205 <RefungibleApproveBasket<T>>::get((collection.id, item_id, who))206 }207 };208209 if let Some(last_tx_block) = last_tx_block {210 let timeout = last_tx_block + limit.into();211 if block_number < timeout {212 return None;213 }214 }215216 match collection.mode {217 CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),218 CollectionMode::Fungible(_) => {219 <FungibleApproveBasket<T>>::insert(collection.id, who, block_number)220 }221 CollectionMode::ReFungible => {222 <RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)223 }224 };225226 Some(())227}228229fn load<T: UniqueConfig>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {230 let collection = CollectionHandle::new(id)?;231 let sponsor = collection.sponsorship.sponsor().cloned()?;232 Some((sponsor, collection))233}234235pub struct UniqueSponsorshipHandler<T>(PhantomData<T>);236impl<T, C> SponsorshipHandler<T::AccountId, C> for UniqueSponsorshipHandler<T>237where238 T: Config,239 C: IsSubType<UniqueCall<T>>,240{241 fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {242 match IsSubType::<UniqueCall<T>>::is_sub_type(call)? {243 UniqueCall::set_token_properties {244 collection_id,245 token_id,246 properties,247 ..248 } => {249 let (sponsor, collection) = load::<T>(*collection_id)?;250 withdraw_set_existing_token_property(251 &collection,252 &T::CrossAccountId::from_sub(who.clone()),253 token_id,254 255 properties.iter().map(|p| p.key.len() + p.value.len()).sum(),256 )257 .map(|()| sponsor)258 }259 UniqueCall::create_item {260 collection_id,261 data,262 ..263 } => {264 let (sponsor, collection) = load(*collection_id)?;265 withdraw_create_item::<T>(266 &collection,267 &T::CrossAccountId::from_sub(who.clone()),268 data,269 )270 .map(|()| sponsor)271 }272 UniqueCall::transfer {273 collection_id,274 item_id,275 ..276 } => {277 let (sponsor, collection) = load(*collection_id)?;278 withdraw_transfer::<T>(279 &collection,280 &T::CrossAccountId::from_sub(who.clone()),281 item_id,282 )283 .map(|()| sponsor)284 }285 UniqueCall::transfer_from {286 collection_id,287 item_id,288 from,289 ..290 } => {291 let (sponsor, collection) = load(*collection_id)?;292 withdraw_transfer::<T>(&collection, from, item_id).map(|()| sponsor)293 }294 UniqueCall::approve {295 collection_id,296 item_id,297 ..298 } => {299 let (sponsor, collection) = load(*collection_id)?;300 withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)301 }302 _ => None,303 }304 }305}306307pub trait SponsorshipPredict<T: Config> {308 fn predict(collection: CollectionId, account: T::CrossAccountId, token: TokenId) -> Option<u64>309 where310 u64: From<<T as frame_system::Config>::BlockNumber>;311}312313pub struct UniqueSponsorshipPredict<T>(PhantomData<T>);314315impl<T: Config> SponsorshipPredict<T> for UniqueSponsorshipPredict<T> {316 fn predict(collection_id: CollectionId, who: T::CrossAccountId, token: TokenId) -> Option<u64>317 where318 u64: From<<T as frame_system::Config>::BlockNumber>,319 {320 let collection = <CollectionHandle<T>>::try_get(collection_id).ok()?;321 let _ = collection.sponsorship.sponsor()?;322323 324 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;325 let limit = collection326 .limits327 .sponsor_transfer_timeout(match collection.mode {328 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,329 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,330 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,331 });332333 let last_tx_block = match collection.mode {334 CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, token),335 CollectionMode::Fungible(_) => {336 <FungibleTransferBasket<T>>::get(collection.id, who.as_sub())337 }338 CollectionMode::ReFungible => {339 <ReFungibleTransferBasket<T>>::get((collection.id, token, who.as_sub()))340 }341 };342343 if let Some(last_tx_block) = last_tx_block {344 return Some(345 last_tx_block346 .saturating_add(limit.into())347 .saturating_sub(block_number)348 .into(),349 );350 }351352 let token_exists = match collection.mode {353 CollectionMode::NFT => {354 <pallet_nonfungible::TokenData<T>>::contains_key((collection.id, token))355 }356 CollectionMode::Fungible(_) => token == TokenId::default(),357 CollectionMode::ReFungible => {358 <pallet_refungible::TotalSupply<T>>::contains_key((collection.id, token))359 }360 };361362 if token_exists {363 Some(0)364 } else {365 None366 }367 }368}