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