git.delta.rocks / unique-network / refs/commits / b80b794104f8

difftreelog

source

runtime/common/src/sponsoring.rs9.1 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use core::marker::PhantomData;18use up_sponsorship::SponsorshipHandler;19use frame_support::{20	traits::{IsSubType},21	storage::{StorageMap, StorageDoubleMap, StorageNMap},22};23use up_data_structs::{24	CollectionId, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,25	NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, TokenId, CollectionMode,26	CreateItemData,27};28use sp_runtime::traits::Saturating;29use pallet_common::{CollectionHandle};30use pallet_evm::account::CrossAccountId;31use pallet_unique::{32	Call as UniqueCall, Config as UniqueConfig, FungibleApproveBasket, RefungibleApproveBasket,33	NftApproveBasket, CreateItemBasket, ReFungibleTransferBasket,34	FungibleTransferBasket, NftTransferBasket,35};36use pallet_fungible::Config as FungibleConfig;37use pallet_nonfungible::Config as NonfungibleConfig;38use pallet_refungible::Config as RefungibleConfig;3940pub trait Config: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig {}41impl<T> Config for T where T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig {}4243pub fn withdraw_transfer<T: Config>(44	collection: &CollectionHandle<T>,45	who: &T::CrossAccountId,46	item_id: &TokenId,47) -> Option<()> {48	// preliminary sponsoring correctness check49	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			if item_id != &TokenId::default() {58				return None;59			}60			if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {61				return None;62			}63		}64		CollectionMode::ReFungible => {65			if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {66				return None;67			}68		}69	}7071	// sponsor timeout72	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;73	let limit = collection74		.limits75		.sponsor_transfer_timeout(match collection.mode {76			CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,77			CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,78			CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,79		});8081	let last_tx_block = match collection.mode {82		CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),83		CollectionMode::Fungible(_) => {84			<FungibleTransferBasket<T>>::get(collection.id, who.as_sub())85		}86		CollectionMode::ReFungible => {87			<ReFungibleTransferBasket<T>>::get((collection.id, item_id, who.as_sub()))88		}89	};9091	if let Some(last_tx_block) = last_tx_block {92		let timeout = last_tx_block + limit.into();93		if block_number < timeout {94			return None;95		}96	}9798	match collection.mode {99		CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),100		CollectionMode::Fungible(_) => {101			<FungibleTransferBasket<T>>::insert(collection.id, who.as_sub(), block_number)102		}103		CollectionMode::ReFungible => <ReFungibleTransferBasket<T>>::insert(104			(collection.id, item_id, who.as_sub()),105			block_number,106		),107	};108109	Some(())110}111112pub fn withdraw_create_item<T: Config>(113	collection: &CollectionHandle<T>,114	who: &T::CrossAccountId,115	_properties: &CreateItemData,116) -> Option<()> {117	if _properties.data_size() as u32 > collection.limits.sponsored_data_size() {118		return None;119	}120121	// sponsor timeout122	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;123	let limit = collection124		.limits125		.sponsor_transfer_timeout(match _properties {126			CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,127			CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,128			CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,129		});130131	if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, who.as_sub())) {132		let timeout = last_tx_block + limit.into();133		if block_number < timeout {134			return None;135		}136	}137138	CreateItemBasket::<T>::insert((collection.id, who.as_sub()), block_number);139140	Some(())141}142143pub fn withdraw_approve<T: Config>(144	collection: &CollectionHandle<T>,145	who: &T::AccountId,146	item_id: &TokenId,147) -> Option<()> {148	// sponsor timeout149	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;150	let limit = collection.limits.sponsor_approve_timeout();151152	let last_tx_block = match collection.mode {153		CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),154		CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),155		CollectionMode::ReFungible => {156			<RefungibleApproveBasket<T>>::get((collection.id, item_id, who))157		}158	};159160	if let Some(last_tx_block) = last_tx_block {161		let timeout = last_tx_block + limit.into();162		if block_number < timeout {163			return None;164		}165	}166167	match collection.mode {168		CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),169		CollectionMode::Fungible(_) => {170			<FungibleApproveBasket<T>>::insert(collection.id, who, block_number)171		}172		CollectionMode::ReFungible => {173			<RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)174		}175	};176177	Some(())178}179180fn load<T: UniqueConfig>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {181	let collection = CollectionHandle::new(id)?;182	let sponsor = collection.sponsorship.sponsor().cloned()?;183	Some((sponsor, collection))184}185186pub struct UniqueSponsorshipHandler<T>(PhantomData<T>);187impl<T, C> SponsorshipHandler<T::AccountId, C> for UniqueSponsorshipHandler<T>188where189	T: Config,190	C: IsSubType<UniqueCall<T>>,191{192	fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {193		match IsSubType::<UniqueCall<T>>::is_sub_type(call)? {194			UniqueCall::create_item {195				collection_id,196				data,197				..198			} => {199				let (sponsor, collection) = load(*collection_id)?;200				withdraw_create_item::<T>(201					&collection,202					&T::CrossAccountId::from_sub(who.clone()),203					data,204				)205				.map(|()| sponsor)206			}207			UniqueCall::transfer {208				collection_id,209				item_id,210				..211			} => {212				let (sponsor, collection) = load(*collection_id)?;213				withdraw_transfer::<T>(214					&collection,215					&T::CrossAccountId::from_sub(who.clone()),216					item_id,217				)218				.map(|()| sponsor)219			}220			UniqueCall::transfer_from {221				collection_id,222				item_id,223				from,224				..225			} => {226				let (sponsor, collection) = load(*collection_id)?;227				withdraw_transfer::<T>(&collection, from, item_id).map(|()| sponsor)228			}229			UniqueCall::approve {230				collection_id,231				item_id,232				..233			} => {234				let (sponsor, collection) = load(*collection_id)?;235				withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)236			}237			_ => None,238		}239	}240}241242pub trait SponsorshipPredict<T: Config> {243	fn predict(collection: CollectionId, account: T::CrossAccountId, token: TokenId) -> Option<u64>244	where245		u64: From<<T as frame_system::Config>::BlockNumber>;246}247248pub struct UniqueSponsorshipPredict<T>(PhantomData<T>);249250impl<T: Config> SponsorshipPredict<T> for UniqueSponsorshipPredict<T> {251	fn predict(collection_id: CollectionId, who: T::CrossAccountId, token: TokenId) -> Option<u64>252	where253		u64: From<<T as frame_system::Config>::BlockNumber>,254	{255		let collection = <CollectionHandle<T>>::try_get(collection_id).ok()?;256		let _ = collection.sponsorship.sponsor()?;257258		// sponsor timeout259		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;260		let limit = collection261			.limits262			.sponsor_transfer_timeout(match collection.mode {263				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,264				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,265				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,266			});267268		let last_tx_block = match collection.mode {269			CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, token),270			CollectionMode::Fungible(_) => {271				<FungibleTransferBasket<T>>::get(collection.id, who.as_sub())272			}273			CollectionMode::ReFungible => {274				<ReFungibleTransferBasket<T>>::get((collection.id, token, who.as_sub()))275			}276		};277278		if let Some(last_tx_block) = last_tx_block {279			return Some(280				last_tx_block281					.saturating_add(limit.into())282					.saturating_sub(block_number)283					.into(),284			);285		}286287		let token_exists = match collection.mode {288			CollectionMode::NFT => {289				<pallet_nonfungible::TokenData<T>>::contains_key((collection.id, token))290			}291			CollectionMode::Fungible(_) => token == TokenId::default(),292			CollectionMode::ReFungible => {293				<pallet_refungible::TotalSupply<T>>::contains_key((collection.id, token))294			}295		};296297		if token_exists {298			Some(0)299		} else {300			None301		}302	}303}