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(ERC721UniqueExtensionsCall::Transfer {39 token_id,40 ..41 })42 | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {43 let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;44 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;45 let collection_limits = &collection.limits;46 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {47 collection_limits.sponsor_transfer_timeout48 } else {49 NFT_SPONSOR_TRANSFER_TIMEOUT50 };5152 let mut sponsor = true;53 if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {54 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);55 let limit_time = last_tx_block + limit.into();56 if block_number <= limit_time {57 sponsor = false;58 }59 }60 if sponsor {61 <NftTransferBasket<T>>::insert(collection_id, token_id, block_number);62 return Ok(());63 }64 }65 _ => {}66 }67 }68 crate::CollectionMode::Fungible(_) => {69 let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)70 .map_err(|_| AnyError)?71 .ok_or(AnyError)?;72 #[allow(clippy::single_match)]73 match call {74 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {75 let who = T::CrossAccountId::from_eth(*caller);76 let collection_limits = &collection.limits;77 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {78 collection_limits.sponsor_transfer_timeout79 } else {80 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT81 };8283 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;84 let mut sponsored = true;85 if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {86 let last_tx_block =87 <FungibleTransferBasket<T>>::get(collection_id, who.as_sub());88 let limit_time = last_tx_block + limit.into();89 if block_number <= limit_time {90 sponsored = false;91 }92 }93 if sponsored {94 <FungibleTransferBasket<T>>::insert(95 collection_id,96 who.as_sub(),97 block_number,98 );99 return Ok(());100 }101 }102 _ => {}103 }104 }105 _ => {}106 }107 Err(AnyError)108}109110pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);111impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {112 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {113 if let Some(collection_id) = map_eth_to_id(&call.0) {114 if let Some(collection) = <CollectionById<T>>::get(collection_id) {115 if !collection.sponsorship.confirmed() {116 return None;117 }118 if try_sponsor(who, collection_id, &collection, &call.1).is_ok() {119 return collection120 .sponsorship121 .sponsor()122 .cloned()123 .map(T::EvmBackwardsAddressMapping::from_account_id);124 }125 }126 }127 None128 }129}