git.delta.rocks / unique-network / refs/commits / 7645db77f3de

difftreelog

source

runtime/common/sponsoring.rs10.5 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};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 {}4041// TODO: permission check?42pub fn withdraw_set_token_property<T: Config>(43	collection: &CollectionHandle<T>,44	who: &T::CrossAccountId,45	item_id: &TokenId,46	data_size: usize,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			// Fungible tokens have no properties58			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	if data_size > collection.limits.sponsored_data_size() as usize {68		return None;69	}7071	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;72	let limit = collection.limits.sponsored_data_rate_limit()?;7374	if let Some(last_tx_block) = TokenPropertyBasket::<T>::get(collection.id, item_id) {75		let timeout = last_tx_block + limit.into();76		if block_number < timeout {77			return None;78		}79	}8081	<TokenPropertyBasket<T>>::insert(collection.id, item_id, block_number);8283	Some(())84}8586pub fn withdraw_transfer<T: Config>(87	collection: &CollectionHandle<T>,88	who: &T::CrossAccountId,89	item_id: &TokenId,90) -> Option<()> {91	// preliminary sponsoring correctness check92	match collection.mode {93		CollectionMode::NFT => {94			let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;95			if !owner.conv_eq(who) {96				return None;97			}98		}99		CollectionMode::Fungible(_) => {100			if item_id != &TokenId::default() {101				return None;102			}103			if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {104				return None;105			}106		}107		CollectionMode::ReFungible => {108			if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {109				return None;110			}111		}112	}113114	// sponsor timeout115	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;116	let limit = collection117		.limits118		.sponsor_transfer_timeout(match collection.mode {119			CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,120			CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,121			CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,122		});123124	let last_tx_block = match collection.mode {125		CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),126		CollectionMode::Fungible(_) => {127			<FungibleTransferBasket<T>>::get(collection.id, who.as_sub())128		}129		CollectionMode::ReFungible => {130			<ReFungibleTransferBasket<T>>::get((collection.id, item_id, who.as_sub()))131		}132	};133134	if let Some(last_tx_block) = last_tx_block {135		let timeout = last_tx_block + limit.into();136		if block_number < timeout {137			return None;138		}139	}140141	match collection.mode {142		CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),143		CollectionMode::Fungible(_) => {144			<FungibleTransferBasket<T>>::insert(collection.id, who.as_sub(), block_number)145		}146		CollectionMode::ReFungible => <ReFungibleTransferBasket<T>>::insert(147			(collection.id, item_id, who.as_sub()),148			block_number,149		),150	};151152	Some(())153}154155pub fn withdraw_create_item<T: Config>(156	collection: &CollectionHandle<T>,157	who: &T::CrossAccountId,158	properties: &CreateItemData,159) -> Option<()> {160	// sponsor timeout161	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;162	let limit = collection163		.limits164		.sponsor_transfer_timeout(match properties {165			CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,166			CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,167			CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,168		});169170	if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, who.as_sub())) {171		let timeout = last_tx_block + limit.into();172		if block_number < timeout {173			return None;174		}175	}176177	CreateItemBasket::<T>::insert((collection.id, who.as_sub()), block_number);178179	Some(())180}181182pub fn withdraw_approve<T: Config>(183	collection: &CollectionHandle<T>,184	who: &T::AccountId,185	item_id: &TokenId,186) -> Option<()> {187	// sponsor timeout188	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;189	let limit = collection.limits.sponsor_approve_timeout();190191	let last_tx_block = match collection.mode {192		CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),193		CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),194		CollectionMode::ReFungible => {195			<RefungibleApproveBasket<T>>::get((collection.id, item_id, who))196		}197	};198199	if let Some(last_tx_block) = last_tx_block {200		let timeout = last_tx_block + limit.into();201		if block_number < timeout {202			return None;203		}204	}205206	match collection.mode {207		CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),208		CollectionMode::Fungible(_) => {209			<FungibleApproveBasket<T>>::insert(collection.id, who, block_number)210		}211		CollectionMode::ReFungible => {212			<RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)213		}214	};215216	Some(())217}218219fn load<T: UniqueConfig>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {220	let collection = CollectionHandle::new(id)?;221	let sponsor = collection.sponsorship.sponsor().cloned()?;222	Some((sponsor, collection))223}224225pub struct UniqueSponsorshipHandler<T>(PhantomData<T>);226impl<T, C> SponsorshipHandler<T::AccountId, C> for UniqueSponsorshipHandler<T>227where228	T: Config,229	C: IsSubType<UniqueCall<T>>,230{231	fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {232		match IsSubType::<UniqueCall<T>>::is_sub_type(call)? {233			UniqueCall::set_token_properties {234				collection_id,235				token_id,236				properties,237				..238			} => {239				let (sponsor, collection) = load::<T>(*collection_id)?;240				withdraw_set_token_property(241					&collection,242					&T::CrossAccountId::from_sub(who.clone()),243					token_id,244					// No overflow may happen, as data larger than usize can't reach here245					properties.iter().map(|p| p.key.len() + p.value.len()).sum(),246				)247				.map(|()| sponsor)248			}249			UniqueCall::create_item {250				collection_id,251				data,252				..253			} => {254				let (sponsor, collection) = load(*collection_id)?;255				withdraw_create_item::<T>(256					&collection,257					&T::CrossAccountId::from_sub(who.clone()),258					data,259				)260				.map(|()| sponsor)261			}262			UniqueCall::transfer {263				collection_id,264				item_id,265				..266			} => {267				let (sponsor, collection) = load(*collection_id)?;268				withdraw_transfer::<T>(269					&collection,270					&T::CrossAccountId::from_sub(who.clone()),271					item_id,272				)273				.map(|()| sponsor)274			}275			UniqueCall::transfer_from {276				collection_id,277				item_id,278				from,279				..280			} => {281				let (sponsor, collection) = load(*collection_id)?;282				withdraw_transfer::<T>(&collection, from, item_id).map(|()| sponsor)283			}284			UniqueCall::approve {285				collection_id,286				item_id,287				..288			} => {289				let (sponsor, collection) = load(*collection_id)?;290				withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)291			}292			_ => None,293		}294	}295}296297pub trait SponsorshipPredict<T: Config> {298	fn predict(collection: CollectionId, account: T::CrossAccountId, token: TokenId) -> Option<u64>299	where300		u64: From<<T as frame_system::Config>::BlockNumber>;301}302303pub struct UniqueSponsorshipPredict<T>(PhantomData<T>);304305impl<T: Config> SponsorshipPredict<T> for UniqueSponsorshipPredict<T> {306	fn predict(collection_id: CollectionId, who: T::CrossAccountId, token: TokenId) -> Option<u64>307	where308		u64: From<<T as frame_system::Config>::BlockNumber>,309	{310		let collection = <CollectionHandle<T>>::try_get(collection_id).ok()?;311		let _ = collection.sponsorship.sponsor()?;312313		// sponsor timeout314		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;315		let limit = collection316			.limits317			.sponsor_transfer_timeout(match collection.mode {318				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,319				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,320				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,321			});322323		let last_tx_block = match collection.mode {324			CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, token),325			CollectionMode::Fungible(_) => {326				<FungibleTransferBasket<T>>::get(collection.id, who.as_sub())327			}328			CollectionMode::ReFungible => {329				<ReFungibleTransferBasket<T>>::get((collection.id, token, who.as_sub()))330			}331		};332333		if let Some(last_tx_block) = last_tx_block {334			return Some(335				last_tx_block336					.saturating_add(limit.into())337					.saturating_sub(block_number)338					.into(),339			);340		}341342		let token_exists = match collection.mode {343			CollectionMode::NFT => {344				<pallet_nonfungible::TokenData<T>>::contains_key((collection.id, token))345			}346			CollectionMode::Fungible(_) => token == TokenId::default(),347			CollectionMode::ReFungible => {348				<pallet_refungible::TotalSupply<T>>::contains_key((collection.id, token))349			}350		};351352		if token_exists {353			Some(0)354		} else {355			None356		}357	}358}