difftreelog
refactor extract evm transaction payment
in: master
3 files changed
pallets/evm-transaction-payment/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/pallets/evm-transaction-payment/Cargo.toml
@@ -0,0 +1,37 @@
+[package]
+name = "pallet-evm-transaction-payment"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+frame-support = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+frame-system = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-runtime = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-std = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-io = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-core = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+pallet-evm = { default-features = false, version = "5.0.0-dev", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" }
+fp-evm = { default-features = false, version = "2.0.0", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" }
+pallet-ethereum = { default-features = false, version = "3.0.0-dev", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" }
+up-sponsorship = { default-features = false, path = '../../primitives/sponsorship' }
+
+[dependencies.codec]
+default-features = false
+features = ['derive']
+package = 'parity-scale-codec'
+version = '2.0.0'
+
+[features]
+default = ["std"]
+std = [
+ "frame-support/std",
+ "frame-system/std",
+ "sp-runtime/std",
+ "sp-std/std",
+ "sp-io/std",
+ "sp-core/std",
+ "pallet-evm/std",
+ "pallet-ethereum/std",
+ "fp-evm/std",
+ "up-sponsorship/std",
+]
pallets/evm-transaction-payment/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/evm-transaction-payment/src/lib.rs
@@ -0,0 +1,98 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use pallet::*;
+
+#[frame_support::pallet]
+pub mod pallet {
+ use core::marker::PhantomData;
+ use frame_support::traits::Currency;
+ use pallet_evm::EVMCurrencyAdapter;
+ use fp_evm::WithdrawReason;
+ use sp_core::{H160, U256};
+ use sp_runtime::TransactionOutcome;
+ use up_sponsorship::SponsorshipHandler;
+ use sp_std::vec::Vec;
+
+ type NegativeImbalanceOf<C, T> =
+ <C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ type SponsorshipHandler: SponsorshipHandler<H160, (H160, Vec<u8>)>;
+ type Currency: Currency<Self::AccountId>;
+ }
+
+ #[pallet::pallet]
+ #[pallet::generate_store(pub(super) trait Store)]
+ pub struct Pallet<T>(_);
+
+ pub struct ChargeEvmLiquidityInfo<T>
+ where
+ T: Config,
+ T: pallet_evm::Config,
+ {
+ who: H160,
+ negative_imbalance: NegativeImbalanceOf<<T as Config>::Currency, T>,
+ }
+
+ pub struct TransactionValidityHack<T: Config>(PhantomData<*const T>);
+ impl<T: Config> fp_evm::TransactionValidityHack for TransactionValidityHack<T> {
+ fn who_pays_fee(origin: H160, reason: &WithdrawReason) -> Option<H160> {
+ match reason {
+ WithdrawReason::Call { target, input } => {
+ // This method is only used for checking, we shouldn't touch storage in it
+ frame_support::storage::with_transaction(|| {
+ TransactionOutcome::Rollback(T::SponsorshipHandler::get_sponsor(
+ &origin,
+ &(*target, input.clone()),
+ ))
+ })
+ }
+ _ => None,
+ }
+ }
+ }
+
+ pub struct OnChargeTransaction<T: Config>(PhantomData<*const T>);
+ impl<T> pallet_evm::OnChargeEVMTransaction<T> for OnChargeTransaction<T>
+ where
+ T: Config,
+ T: pallet_evm::Config,
+ {
+ type LiquidityInfo = Option<ChargeEvmLiquidityInfo<T>>;
+
+ fn withdraw_fee(
+ who: &H160,
+ reason: WithdrawReason,
+ fee: U256,
+ ) -> core::result::Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
+ let mut who_pays_fee = *who;
+ if let WithdrawReason::Call { target, input } = &reason {
+ who_pays_fee = T::SponsorshipHandler::get_sponsor(who, &(*target, input.clone()))
+ .unwrap_or(who_pays_fee);
+ }
+ let negative_imbalance =
+ EVMCurrencyAdapter::<<T as Config>::Currency, ()>::withdraw_fee(
+ &who_pays_fee,
+ reason,
+ fee,
+ )?;
+ Ok(negative_imbalance.map(|i| ChargeEvmLiquidityInfo {
+ who: who_pays_fee,
+ negative_imbalance: i,
+ }))
+ }
+
+ fn correct_and_deposit_fee(
+ who: &H160,
+ corrected_fee: U256,
+ already_withdrawn: Self::LiquidityInfo,
+ ) -> core::result::Result<(), pallet_evm::Error<T>> {
+ EVMCurrencyAdapter::<<T as Config>::Currency, ()>::correct_and_deposit_fee(
+ &already_withdrawn.as_ref().map(|e| e.who).unwrap_or(*who),
+ corrected_fee,
+ already_withdrawn.map(|e| e.negative_imbalance),
+ )
+ }
+ }
+}
pallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth1//! Implements EVM sponsoring logic via OnChargeEVMTransaction23use crate::{4 Collection, ChainLimit, CollectionById, Config, NftTransferBasket, FungibleTransferBasket,5 eth::account::EvmBackwardsAddressMapping,6};7use evm_coder::abi::AbiReader;8use frame_support::{9 storage::{StorageMap, StorageDoubleMap, StorageValue},10 traits::Currency,11};12use pallet_evm::{EVMCurrencyAdapter, WithdrawReason};13use sp_core::{H160, U256};14use sp_std::prelude::*;15use super::{16 account::CrossAccountId,17 erc::{UniqueFungibleCall, UniqueNFTCall, ERC721Call, ERC20Call, ERC721UniqueExtensionsCall},18};19use core::convert::TryInto;2021type NegativeImbalanceOf<C, T> =22 <C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;2324pub struct ChargeEvmTransaction;25pub struct ChargeEvmLiquidityInfo<T>26where27 T: Config,28 T: pallet_evm::Config,29{30 who: H160,31 negative_imbalance: NegativeImbalanceOf<<T as Config>::Currency, T>,32}3334struct AnyError;3536fn try_sponsor<T: Config>(37 caller: &H160,38 collection_id: u32,39 collection: &Collection<T>,40 call: &[u8],41) -> Result<(), AnyError> {42 let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;43 match &collection.mode {44 crate::CollectionMode::NFT => {45 let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)46 .map_err(|_| AnyError)?47 .ok_or(AnyError)?;48 match call {49 UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {50 token_id,51 ..52 })53 | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {54 let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;55 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;56 let collection_limits = &collection.limits;57 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {58 collection_limits.sponsor_transfer_timeout59 } else {60 ChainLimit::get().nft_sponsor_transfer_timeout61 };6263 let mut sponsor = true;64 if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {65 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);66 let limit_time = last_tx_block + limit.into();67 if block_number <= limit_time {68 sponsor = false;69 }70 }71 if sponsor {72 <NftTransferBasket<T>>::insert(collection_id, token_id, block_number);73 return Ok(());74 }75 }76 _ => {}77 }78 }79 crate::CollectionMode::Fungible(_) => {80 let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)81 .map_err(|_| AnyError)?82 .ok_or(AnyError)?;83 #[allow(clippy::single_match)]84 match call {85 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {86 let who = T::CrossAccountId::from_eth(*caller);87 let collection_limits = &collection.limits;88 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {89 collection_limits.sponsor_transfer_timeout90 } else {91 ChainLimit::get().fungible_sponsor_transfer_timeout92 };9394 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;95 let mut sponsored = true;96 if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {97 let last_tx_block =98 <FungibleTransferBasket<T>>::get(collection_id, who.as_sub());99 let limit_time = last_tx_block + limit.into();100 if block_number <= limit_time {101 sponsored = false;102 }103 }104 if sponsored {105 <FungibleTransferBasket<T>>::insert(106 collection_id,107 who.as_sub(),108 block_number,109 );110 return Ok(());111 }112 }113 _ => {}114 }115 }116 _ => {}117 }118 Err(AnyError)119}120121impl<T> pallet_evm::OnChargeEVMTransaction<T> for ChargeEvmTransaction122where123 T: Config,124 T: pallet_evm::Config,125{126 type LiquidityInfo = Option<ChargeEvmLiquidityInfo<T>>;127128 fn withdraw_fee(129 who: &H160,130 reason: WithdrawReason,131 fee: U256,132 ) -> Result<Self::LiquidityInfo, pallet_evm::Error<T>> {133 let mut who_pays_fee = *who;134 if let WithdrawReason::Call { target, input } = &reason {135 if let Some(collection_id) = crate::eth::map_eth_to_id(target) {136 if let Some(collection) = <CollectionById<T>>::get(collection_id) {137 if let Some(sponsor) = collection.sponsorship.sponsor() {138 if try_sponsor(who, collection_id, &collection, input).is_ok() {139 who_pays_fee =140 T::EvmBackwardsAddressMapping::from_account_id(sponsor.clone());141 }142 }143 }144 }145 }146147 // TODO: Pay fee from substrate address148 let negative_imbalance = EVMCurrencyAdapter::<<T as Config>::Currency, ()>::withdraw_fee(149 &who_pays_fee,150 reason,151 fee,152 )?;153 Ok(negative_imbalance.map(|i| ChargeEvmLiquidityInfo {154 who: who_pays_fee,155 negative_imbalance: i,156 }))157 }158159 fn correct_and_deposit_fee(160 who: &H160,161 corrected_fee: U256,162 already_withdrawn: Self::LiquidityInfo,163 ) -> Result<(), pallet_evm::Error<T>> {164 EVMCurrencyAdapter::<<T as Config>::Currency, ()>::correct_and_deposit_fee(165 &already_withdrawn.as_ref().map(|e| e.who).unwrap_or(*who),166 corrected_fee,167 already_withdrawn.map(|e| e.negative_imbalance),168 )169 }170}1//! Implements EVM sponsoring logic via OnChargeEVMTransaction23use 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}