git.delta.rocks / unique-network / refs/commits / 012eb4335d15

difftreelog

source

pallets/nft/src/eth/sponsoring.rs5.0 KiBsourcehistory
1//! Implements EVM sponsoring logic via OnChargeEVMTransaction23use crate::{4	Collection, ChainLimit, CollectionById, Config, NftTransferBasket, FungibleTransferBasket,5	eth::account::EvmBackwardsAddressMapping,6};7use evm_coder::abi::AbiReader;8use frame_support::{9	storage::{StorageMap, StorageDoubleMap, StorageValue},10	traits::Currency,11};12use pallet_evm::{EVMCurrencyAdapter, WithdrawReason};13use sp_core::{H160, U256};14use sp_std::prelude::*;15use super::{16	account::CrossAccountId,17	erc::{UniqueFungibleCall, UniqueNFTCall, ERC721Call, ERC20Call, ERC721UniqueExtensionsCall},18};19use core::convert::TryInto;2021type NegativeImbalanceOf<C, T> =22	<C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;2324pub struct ChargeEvmTransaction;25pub struct ChargeEvmLiquidityInfo<T>26where27	T: Config,28	T: pallet_evm::Config,29{30	who: H160,31	negative_imbalance: NegativeImbalanceOf<<T as Config>::Currency, T>,32}3334struct AnyError;3536fn try_sponsor<T: Config>(37	caller: &H160,38	collection_id: u32,39	collection: &Collection<T>,40	call: &[u8],41) -> Result<(), AnyError> {42	let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;43	match &collection.mode {44		crate::CollectionMode::NFT => {45			let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)46				.map_err(|_| AnyError)?47				.ok_or(AnyError)?;48			match call {49				UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {50					token_id,51					..52				})53				| UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {54					let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;55					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;56					let collection_limits = &collection.limits;57					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {58						collection_limits.sponsor_transfer_timeout59					} else {60						ChainLimit::get().nft_sponsor_transfer_timeout61					};6263					let mut sponsor = true;64					if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {65						let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);66						let limit_time = last_tx_block + limit.into();67						if block_number <= limit_time {68							sponsor = false;69						}70					}71					if sponsor {72						<NftTransferBasket<T>>::insert(collection_id, token_id, block_number);73						return Ok(());74					}75				}76				_ => {}77			}78		}79		crate::CollectionMode::Fungible(_) => {80			let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)81				.map_err(|_| AnyError)?82				.ok_or(AnyError)?;83			#[allow(clippy::single_match)]84			match call {85				UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {86					let who = T::CrossAccountId::from_eth(*caller);87					let collection_limits = &collection.limits;88					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {89						collection_limits.sponsor_transfer_timeout90					} else {91						ChainLimit::get().fungible_sponsor_transfer_timeout92					};9394					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;95					let mut sponsored = true;96					if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {97						let last_tx_block =98							<FungibleTransferBasket<T>>::get(collection_id, who.as_sub());99						let limit_time = last_tx_block + limit.into();100						if block_number <= limit_time {101							sponsored = false;102						}103					}104					if sponsored {105						<FungibleTransferBasket<T>>::insert(106							collection_id,107							who.as_sub(),108							block_number,109						);110						return Ok(());111					}112				}113				_ => {}114			}115		}116		_ => {}117	}118	Err(AnyError)119}120121impl<T> pallet_evm::OnChargeEVMTransaction<T> for ChargeEvmTransaction122where123	T: Config,124	T: pallet_evm::Config,125{126	type LiquidityInfo = Option<ChargeEvmLiquidityInfo<T>>;127128	fn withdraw_fee(129		who: &H160,130		reason: WithdrawReason,131		fee: U256,132	) -> Result<Self::LiquidityInfo, pallet_evm::Error<T>> {133		let mut who_pays_fee = *who;134		if let WithdrawReason::Call { target, input } = &reason {135			if let Some(collection_id) = crate::eth::map_eth_to_id(target) {136				if let Some(collection) = <CollectionById<T>>::get(collection_id) {137					if let Some(sponsor) = collection.sponsorship.sponsor() {138						if try_sponsor(who, collection_id, &collection, input).is_ok() {139							who_pays_fee =140								T::EvmBackwardsAddressMapping::from_account_id(sponsor.clone());141						}142					}143				}144			}145		}146147		// TODO: Pay fee from substrate address148		let negative_imbalance = EVMCurrencyAdapter::<<T as Config>::Currency, ()>::withdraw_fee(149			&who_pays_fee,150			reason,151			fee,152		)?;153		Ok(negative_imbalance.map(|i| ChargeEvmLiquidityInfo {154			who: who_pays_fee,155			negative_imbalance: i,156		}))157	}158159	fn correct_and_deposit_fee(160		who: &H160,161		corrected_fee: U256,162		already_withdrawn: Self::LiquidityInfo,163	) -> Result<(), pallet_evm::Error<T>> {164		EVMCurrencyAdapter::<<T as Config>::Currency, ()>::correct_and_deposit_fee(165			&already_withdrawn.as_ref().map(|e| e.who).unwrap_or(*who),166			corrected_fee,167			already_withdrawn.map(|e| e.negative_imbalance),168		)169	}170}