git.delta.rocks / unique-network / refs/commits / c12e121fed0b

difftreelog

source

pallets/nft/src/eth/sponsoring.rs6.0 KiBsourcehistory
1//! Implements EVM sponsoring logic via OnChargeEVMTransaction23use 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        // TODO: Pay fee from substrate address120        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}