123use crate::{Collection, Config, FungibleTransferBasket, NftTransferBasket};4use evm_coder::{Call, abi::AbiReader};5use frame_support::{6 storage::{StorageDoubleMap},7};8use pallet_common::eth::map_eth_to_id;9use sp_core::H160;10use sp_std::prelude::*;11use up_sponsorship::SponsorshipHandler;12use core::marker::PhantomData;13use core::convert::TryInto;14use nft_data_structs::{CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};15use pallet_common::{16 CollectionById,17 account::{CrossAccountId, EvmBackwardsAddressMapping},18};1920use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};21use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};2223struct AnyError;2425fn try_sponsor<T: Config>(26 caller: &H160,27 collection_id: CollectionId,28 collection: &Collection<T>,29 call: &[u8],30) -> Result<(), AnyError> {31 let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;32 match &collection.mode {33 crate::CollectionMode::NFT => {34 let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)35 .map_err(|_| AnyError)?36 .ok_or(AnyError)?;37 match call {38 UniqueNFTCall::ERC721UniqueExtensions(39 ERC721UniqueExtensionsCall::TransferNft { token_id, .. },40 )41 | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {42 let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;43 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;44 let collection_limits = &collection.limits;45 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {46 collection_limits.sponsor_transfer_timeout47 } else {48 NFT_SPONSOR_TRANSFER_TIMEOUT49 };5051 let mut sponsor = true;52 if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {53 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);54 let limit_time = last_tx_block + limit.into();55 if block_number <= limit_time {56 sponsor = false;57 }58 }59 if sponsor {60 <NftTransferBasket<T>>::insert(collection_id, token_id, block_number);61 return Ok(());62 }63 }64 _ => {}65 }66 }67 crate::CollectionMode::Fungible(_) => {68 let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)69 .map_err(|_| AnyError)?70 .ok_or(AnyError)?;71 #[allow(clippy::single_match)]72 match call {73 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {74 let who = T::CrossAccountId::from_eth(*caller);75 let collection_limits = &collection.limits;76 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {77 collection_limits.sponsor_transfer_timeout78 } else {79 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT80 };8182 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;83 let mut sponsored = true;84 if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {85 let last_tx_block =86 <FungibleTransferBasket<T>>::get(collection_id, who.as_sub());87 let limit_time = last_tx_block + limit.into();88 if block_number <= limit_time {89 sponsored = false;90 }91 }92 if sponsored {93 <FungibleTransferBasket<T>>::insert(94 collection_id,95 who.as_sub(),96 block_number,97 );98 return Ok(());99 }100 }101 _ => {}102 }103 }104 _ => {}105 }106 Err(AnyError)107}108109pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);110impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {111 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {112 if let Some(collection_id) = map_eth_to_id(&call.0) {113 if let Some(collection) = <CollectionById<T>>::get(collection_id) {114 if !collection.sponsorship.confirmed() {115 return None;116 }117 if try_sponsor(who, collection_id, &collection, &call.1).is_ok() {118 return collection119 .sponsorship120 .sponsor()121 .cloned()122 .map(T::EvmBackwardsAddressMapping::from_account_id);123 }124 }125 }126 None127 }128}