git.delta.rocks / unique-network / refs/commits / 008a188c3ea9

difftreelog

source

runtime/common/src/sponsoring.rs10.9 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, MetaUpdatePermission,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, VariableMetaDataBasket, 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_set_variable_meta_data<T: Config>(144	who: &T::CrossAccountId,145	collection: &CollectionHandle<T>,146	item_id: &TokenId,147	data: &[u8],148) -> Option<()> {149	// TODO: make it work for admins150	if collection.meta_update_permission != MetaUpdatePermission::ItemOwner {151		return None;152	}153	// preliminary sponsoring correctness check154	match collection.mode {155		CollectionMode::NFT => {156			let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;157			if !owner.conv_eq(who) {158				return None;159			}160		}161		CollectionMode::Fungible(_) => {162			if item_id != &TokenId::default() {163				return None;164			}165			if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {166				return None;167			}168		}169		CollectionMode::ReFungible => {170			if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {171				return None;172			}173		}174	}175176	// Can't sponsor fungible collection, this tx will be rejected177	// as invalid178	if matches!(collection.mode, CollectionMode::Fungible(_)) {179		return None;180	}181	if data.len() > collection.limits.sponsored_data_size() as usize {182		return None;183	}184185	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;186	let limit = collection.limits.sponsored_data_rate_limit()?;187188	if let Some(last_tx_block) = VariableMetaDataBasket::<T>::get(collection.id, item_id) {189		let timeout = last_tx_block + limit.into();190		if block_number < timeout {191			return None;192		}193	}194195	<VariableMetaDataBasket<T>>::insert(collection.id, item_id, block_number);196197	Some(())198}199200pub fn withdraw_approve<T: Config>(201	collection: &CollectionHandle<T>,202	who: &T::AccountId,203	item_id: &TokenId,204) -> Option<()> {205	// sponsor timeout206	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;207	let limit = collection.limits.sponsor_approve_timeout();208209	let last_tx_block = match collection.mode {210		CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),211		CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),212		CollectionMode::ReFungible => {213			<RefungibleApproveBasket<T>>::get((collection.id, item_id, who))214		}215	};216217	if let Some(last_tx_block) = last_tx_block {218		let timeout = last_tx_block + limit.into();219		if block_number < timeout {220			return None;221		}222	}223224	match collection.mode {225		CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),226		CollectionMode::Fungible(_) => {227			<FungibleApproveBasket<T>>::insert(collection.id, who, block_number)228		}229		CollectionMode::ReFungible => {230			<RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)231		}232	};233234	Some(())235}236237fn load<T: UniqueConfig>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {238	let collection = CollectionHandle::new(id)?;239	let sponsor = collection.sponsorship.sponsor().cloned()?;240	Some((sponsor, collection))241}242243pub struct UniqueSponsorshipHandler<T>(PhantomData<T>);244impl<T, C> SponsorshipHandler<T::AccountId, C> for UniqueSponsorshipHandler<T>245where246	T: Config,247	C: IsSubType<UniqueCall<T>>,248{249	fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {250		match IsSubType::<UniqueCall<T>>::is_sub_type(call)? {251			UniqueCall::create_item {252				collection_id,253				data,254				..255			} => {256				let (sponsor, collection) = load(*collection_id)?;257				withdraw_create_item::<T>(258					&collection,259					&T::CrossAccountId::from_sub(who.clone()),260					data,261				)262				.map(|()| sponsor)263			}264			UniqueCall::transfer {265				collection_id,266				item_id,267				..268			} => {269				let (sponsor, collection) = load(*collection_id)?;270				withdraw_transfer::<T>(271					&collection,272					&T::CrossAccountId::from_sub(who.clone()),273					item_id,274				)275				.map(|()| sponsor)276			}277			UniqueCall::transfer_from {278				collection_id,279				item_id,280				from,281				..282			} => {283				let (sponsor, collection) = load(*collection_id)?;284				withdraw_transfer::<T>(&collection, from, item_id).map(|()| sponsor)285			}286			UniqueCall::approve {287				collection_id,288				item_id,289				..290			} => {291				let (sponsor, collection) = load(*collection_id)?;292				withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)293			}294			UniqueCall::set_variable_meta_data {295				collection_id,296				item_id,297				data,298			} => {299				let (sponsor, collection) = load(*collection_id)?;300				withdraw_set_variable_meta_data::<T>(301					&T::CrossAccountId::from_sub(who.clone()),302					&collection,303					item_id,304					data,305				)306				.map(|()| sponsor)307			}308			_ => None,309		}310	}311}312313pub trait SponsorshipPredict<T: Config> {314	fn predict(collection: CollectionId, account: T::CrossAccountId, token: TokenId) -> Option<u64>315	where316		u64: From<<T as frame_system::Config>::BlockNumber>;317}318319pub struct UniqueSponsorshipPredict<T>(PhantomData<T>);320321impl<T: Config> SponsorshipPredict<T> for UniqueSponsorshipPredict<T> {322	fn predict(collection_id: CollectionId, who: T::CrossAccountId, token: TokenId) -> Option<u64>323	where324		u64: From<<T as frame_system::Config>::BlockNumber>,325	{326		let collection = <CollectionHandle<T>>::try_get(collection_id).ok()?;327		let _ = collection.sponsorship.sponsor()?;328329		// sponsor timeout330		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;331		let limit = collection332			.limits333			.sponsor_transfer_timeout(match collection.mode {334				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,335				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,336				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,337			});338339		let last_tx_block = match collection.mode {340			CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, token),341			CollectionMode::Fungible(_) => {342				<FungibleTransferBasket<T>>::get(collection.id, who.as_sub())343			}344			CollectionMode::ReFungible => {345				<ReFungibleTransferBasket<T>>::get((collection.id, token, who.as_sub()))346			}347		};348349		if let Some(last_tx_block) = last_tx_block {350			return Some(351				last_tx_block352					.saturating_add(limit.into())353					.saturating_sub(block_number)354					.into(),355			);356		}357358		let token_exists = match collection.mode {359			CollectionMode::NFT => {360				<pallet_nonfungible::TokenData<T>>::contains_key((collection.id, token))361			}362			CollectionMode::Fungible(_) => token == TokenId::default(),363			CollectionMode::ReFungible => {364				<pallet_refungible::TotalSupply<T>>::contains_key((collection.id, token))365			}366		};367368		if token_exists {369			Some(0)370		} else {371			None372		}373	}374}