123use crate::{4 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},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;20use nft_data_structs::{NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};2122struct AnyError;2324fn try_sponsor<T: Config>(25 caller: &H160,26 collection_id: u32,27 collection: &Collection<T>,28 call: &[u8],29) -> Result<(), AnyError> {30 let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;31 match &collection.mode {32 crate::CollectionMode::NFT => {33 let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)34 .map_err(|_| AnyError)?35 .ok_or(AnyError)?;36 match call {37 UniqueNFTCall::ERC721UniqueExtensions(38 ERC721UniqueExtensionsCall::TransferNft { token_id, .. },39 )40 | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {41 let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;42 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;43 let collection_limits = &collection.limits;44 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {45 collection_limits.sponsor_transfer_timeout46 } else {47 NFT_SPONSOR_TRANSFER_TIMEOUT48 };4950 let mut sponsor = true;51 if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {52 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);53 let limit_time = last_tx_block + limit.into();54 if block_number <= limit_time {55 sponsor = false;56 }57 }58 if sponsor {59 <NftTransferBasket<T>>::insert(collection_id, token_id, block_number);60 return Ok(());61 }62 }63 _ => {}64 }65 }66 crate::CollectionMode::Fungible(_) => {67 let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)68 .map_err(|_| AnyError)?69 .ok_or(AnyError)?;70 #[allow(clippy::single_match)]71 match call {72 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {73 let who = T::CrossAccountId::from_eth(*caller);74 let collection_limits = &collection.limits;75 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {76 collection_limits.sponsor_transfer_timeout77 } else {78 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT79 };8081 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;82 let mut sponsored = true;83 if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {84 let last_tx_block =85 <FungibleTransferBasket<T>>::get(collection_id, who.as_sub());86 let limit_time = last_tx_block + limit.into();87 if block_number <= limit_time {88 sponsored = false;89 }90 }91 if sponsored {92 <FungibleTransferBasket<T>>::insert(93 collection_id,94 who.as_sub(),95 block_number,96 );97 return Ok(());98 }99 }100 _ => {}101 }102 }103 _ => {}104 }105 Err(AnyError)106}107108pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);109impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {110 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {111 if let Some(collection_id) = map_eth_to_id(&call.0) {112 if let Some(collection) = <CollectionById<T>>::get(collection_id) {113 if !collection.sponsorship.confirmed() {114 return None;115 }116 if try_sponsor(who, collection_id, &collection, &call.1).is_ok() {117 return collection118 .sponsorship119 .sponsor()120 .cloned()121 .map(T::EvmBackwardsAddressMapping::from_account_id);122 }123 }124 }125 None126 }127}