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<T>>::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 =47 collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);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<T>>::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 = collection_limits75 .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);7677 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;78 let mut sponsored = true;79 if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {80 let last_tx_block =81 <FungibleTransferBasket<T>>::get(collection_id, who.as_sub());82 let limit_time = last_tx_block + limit.into();83 if block_number <= limit_time {84 sponsored = false;85 }86 }87 if sponsored {88 <FungibleTransferBasket<T>>::insert(89 collection_id,90 who.as_sub(),91 block_number,92 );93 return Ok(());94 }95 }96 _ => {}97 }98 }99 _ => {}100 }101 Err(AnyError)102}103104pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);105impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {106 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {107 if let Some(collection_id) = map_eth_to_id(&call.0) {108 if let Some(collection) = <CollectionById<T>>::get(collection_id) {109 if !collection.sponsorship.confirmed() {110 return None;111 }112 if try_sponsor(who, collection_id, &collection, &call.1).is_ok() {113 return collection114 .sponsorship115 .sponsor()116 .cloned()117 .map(T::EvmBackwardsAddressMapping::from_account_id);118 }119 }120 }121 None122 }123}