123use crate::{Collection, ChainLimit, CollectionById, Config, NftTransferBasket, FungibleTransferBasket, eth::account::EvmBackwardsAddressMapping};4use evm_coder::abi::AbiReader;5use frame_support::{storage::{StorageMap, StorageDoubleMap, StorageValue}, traits::Currency};6use pallet_evm::{EVMCurrencyAdapter, WithdrawReason};7use sp_core::{H160, U256};8use sp_std::prelude::*;9use super::{account::CrossAccountId, erc::{UniqueFungibleCall, UniqueNFTCall, ERC721Call, ERC20Call, ERC721UniqueExtensionsCall}};10use core::convert::TryInto;1112type NegativeImbalanceOf<C, T> =13 <C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;1415pub struct ChargeEvmTransaction;16pub struct ChargeEvmLiquidityInfo<T>17where18 T: Config,19 T: pallet_evm::Config,20{21 who: H160,22 negative_imbalance: NegativeImbalanceOf<<T as Config>::Currency, T>,23}2425struct AnyError;2627fn try_sponsor<T: Config>(caller: &H160, collection_id: u32, collection: &Collection<T>, call: &[u8]) -> Result<(), AnyError> {28 let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;29 match &collection.mode {30 crate::CollectionMode::NFT => {31 let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader).map_err(|_| AnyError)?.ok_or(AnyError)?;32 match call {33 UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {token_id, ..}) | UniqueNFTCall::ERC721(ERC721Call::TransferFrom {token_id, ..}) => {34 let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;35 let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;36 let collection_limits = &collection.limits;37 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {38 collection_limits.sponsor_transfer_timeout39 } else {40 ChainLimit::get().nft_sponsor_transfer_timeout41 };4243 let mut sponsor = true;44 if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {45 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);46 let limit_time = last_tx_block + limit.into();47 if block_number <= limit_time {48 sponsor = false;49 }50 }51 if sponsor {52 <NftTransferBasket<T>>::insert(collection_id, token_id, block_number);53 return Ok(())54 }55 },56 _ => {},57 }58 },59 crate::CollectionMode::Fungible(_) => {60 let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader).map_err(|_| AnyError)?.ok_or(AnyError)?;61 match call {62 UniqueFungibleCall::ERC20(ERC20Call::Transfer {..}) => {63 let who = T::CrossAccountId::from_eth(caller.clone());64 let collection_limits = &collection.limits;65 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {66 collection_limits.sponsor_transfer_timeout67 } else {68 ChainLimit::get().fungible_sponsor_transfer_timeout69 };7071 let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;72 let mut sponsored = true;73 if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {74 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who.as_sub());75 let limit_time = last_tx_block + limit.into();76 if block_number <= limit_time {77 sponsored = false;78 }79 }80 if sponsored {81 <FungibleTransferBasket<T>>::insert(collection_id, who.as_sub(), block_number);82 return Ok(())83 }84 },85 _ => {},86 }87 },88 _ => {},89 }90 return Err(AnyError)91}9293impl<T> pallet_evm::OnChargeEVMTransaction<T> for ChargeEvmTransaction94where95 T: Config,96 T: pallet_evm::Config,97{98 type LiquidityInfo = Option<ChargeEvmLiquidityInfo<T>>;99100 fn withdraw_fee(101 who: &H160,102 reason: WithdrawReason,103 fee: U256,104 ) -> Result<Self::LiquidityInfo, pallet_evm::Error<T>> {105 let mut who_pays_fee = *who;106 if let WithdrawReason::Call { target, input } = &reason {107 if let Some(collection_id) = crate::eth::map_eth_to_id(&target) {108 if let Some(collection) = <CollectionById<T>>::get(collection_id) {109 if let Some(sponsor) = collection.sponsorship.sponsor() {110 if try_sponsor(who, collection_id, &collection, &input).is_ok() {111 who_pays_fee =112 T::EvmBackwardsAddressMapping::from_account_id(sponsor.clone());113 }114 }115 }116 }117 }118119 120 let negative_imbalance = EVMCurrencyAdapter::<<T as Config>::Currency, ()>::withdraw_fee(121 &who_pays_fee,122 reason,123 fee,124 )?;125 Ok(negative_imbalance.map(|i| ChargeEvmLiquidityInfo {126 who: who_pays_fee,127 negative_imbalance: i,128 }))129 }130131 fn correct_and_deposit_fee(132 who: &H160,133 corrected_fee: U256,134 already_withdrawn: Self::LiquidityInfo,135 ) -> Result<(), pallet_evm::Error<T>> {136 EVMCurrencyAdapter::<<T as Config>::Currency, ()>::correct_and_deposit_fee(137 &already_withdrawn.as_ref().map(|e| e.who).unwrap_or(*who),138 corrected_fee,139 already_withdrawn.map(|e| e.negative_imbalance),140 )141 }142}